From 6b814fb973cbeb579b20fdd44cdcb1e7a2526c58 Mon Sep 17 00:00:00 2001 From: GreemDev Date: Tue, 11 Nov 2025 12:55:36 -0600 Subject: [PATCH] feature: .NET 10 (ryubing/ryujinx!214) See merge request ryubing/ryujinx!214 --- COMPILING.md | 2 +- Directory.Build.props | 4 +- Ryujinx.sln | 1 + global.json | 2 +- src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs | 6 +- src/ARMeilleure/CodeGen/Arm64/Assembler.cs | 10 +- .../CodeGen/Arm64/CodeGenerator.cs | 82 +- .../CodeGen/Arm64/CodeGeneratorIntrinsic.cs | 4 +- src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs | 26 +- .../CodeGen/Optimizations/BlockPlacement.cs | 2 +- .../CodeGen/Optimizations/Optimizer.cs | 2 +- .../CodeGen/Optimizations/Simplification.cs | 4 +- .../RegisterAllocators/CopyResolver.cs | 2 +- .../RegisterAllocators/HybridAllocator.cs | 18 +- .../RegisterAllocators/LinearScanAllocator.cs | 4 +- .../RegisterAllocators/StackAllocator.cs | 2 +- src/ARMeilleure/CodeGen/X86/Assembler.cs | 6 +- src/ARMeilleure/CodeGen/X86/CodeGenerator.cs | 98 +- src/ARMeilleure/CodeGen/X86/PreAllocator.cs | 16 +- .../CodeGen/X86/PreAllocatorSystemV.cs | 20 +- .../CodeGen/X86/PreAllocatorWindows.cs | 14 +- src/ARMeilleure/CodeGen/X86/X86Condition.cs | 6 +- src/ARMeilleure/Decoders/Condition.cs | 4 +- .../Instructions/InstEmitHashHelper.cs | 2 +- .../Instructions/InstEmitMemoryHelper.cs | 14 +- src/ARMeilleure/Instructions/SoftFallback.cs | 692 --- .../SoftFallback/SoftFallback.Aes.cs | 32 + .../SoftFallback/SoftFallback.Count.cs | 50 + .../SoftFallback/SoftFallback.Crc32.cs | 74 + .../SoftFallback/SoftFallback.Saturation.cs | 103 + .../SoftFallback/SoftFallback.Sha1.cs | 131 + .../SoftFallback/SoftFallback.Sha256.cs | 140 + .../SoftFallback/SoftFallback.ShrImm64.cs | 93 + .../SoftFallback/SoftFallback.Table.cs | 88 + .../Instructions/SoftFallback/SoftFallback.cs | 26 + src/ARMeilleure/Instructions/SoftFloat.cs | 3735 ----------------- .../Instructions/SoftFloat/SoftFloat.cs | 111 + .../Instructions/SoftFloat/SoftFloat16.cs | 212 + .../Instructions/SoftFloat/SoftFloat16_32.cs | 182 + .../Instructions/SoftFloat/SoftFloat16_64.cs | 182 + .../Instructions/SoftFloat/SoftFloat32.cs | 1421 +++++++ .../Instructions/SoftFloat/SoftFloat32_16.cs | 126 + .../Instructions/SoftFloat/SoftFloat64.cs | 1421 +++++++ .../Instructions/SoftFloat/SoftFloat64_16.cs | 127 + .../IntermediateRepresentation/Comparison.cs | 4 +- .../IntermediateRepresentation/OperandType.cs | 32 +- src/ARMeilleure/Memory/MemoryManagerType.cs | 17 +- src/ARMeilleure/Translation/Translator.cs | 8 +- .../CompatLayerHardwareDeviceSession.cs | 6 +- .../Integration/HardwareDeviceImpl.cs | 2 +- src/Ryujinx.Cpu/Jit/JitCpuContext.cs | 2 +- .../LightningJit/Arm32/ScopedRegister.cs | 2 +- .../Arm32/Target/Arm64/Compiler.cs | 2 +- .../Arm32/Target/Arm64/InstEmitFlow.cs | 2 +- .../Arm32/Target/Arm64/InstEmitMemory.cs | 4 +- src/Ryujinx.Cpu/LightningJit/Arm64/Block.cs | 2 +- .../LightningJit/Arm64/InstName.cs | 139 +- .../LightningJit/Arm64/RegisterAllocator.cs | 2 +- .../LightningJit/Arm64/RegisterUtils.cs | 4 +- .../Arm64/Target/Arm64/Compiler.cs | 4 +- .../Arm64/Target/Arm64/Decoder.cs | 12 +- .../Arm64/Target/Arm64/InstEmitMemory.cs | 6 +- .../CodeGen/Arm64/ArmCondition.cs | 4 +- .../LightningJit/CodeGen/Arm64/Assembler.cs | 8 +- .../CodeGen/Arm64/RegisterSaveRestore.cs | 14 +- .../LightningJit/CodeGen/OperandType.cs | 9 +- src/Ryujinx.Cpu/LightningJit/Translator.cs | 2 +- src/Ryujinx.Graphics.GAL/BlendFactor.cs | 20 +- src/Ryujinx.Graphics.GAL/BufferHandle.cs | 3 + src/Ryujinx.Graphics.GAL/Format.cs | 736 +--- src/Ryujinx.Graphics.GAL/Target.cs | 21 +- .../InlineToMemory/InlineToMemoryClass.cs | 2 +- .../Threed/ComputeDraw/VtgAsComputeState.cs | 2 +- .../Engine/Threed/DrawManager.cs | 4 +- .../Threed/SpecializationStateUpdater.cs | 4 +- .../Engine/Threed/StateUpdater.cs | 18 +- .../Engine/Twod/TwodClass.cs | 2 +- src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs | 2 +- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 10 +- .../Image/TextureCache.cs | 2 +- .../Image/TextureCompatibility.cs | 13 +- .../Image/TextureGroup.cs | 2 +- src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 2 +- src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 2 +- src/Ryujinx.Graphics.OpenGL/Buffer.cs | 14 +- src/Ryujinx.Graphics.OpenGL/Handle.cs | 6 - .../Image/FormatConverter.cs | 2 +- .../Image/TextureBuffer.cs | 2 +- .../Image/TextureCopy.cs | 4 +- .../Image/TextureView.cs | 50 +- .../PersistentBuffers.cs | 6 +- src/Ryujinx.Graphics.OpenGL/Pipeline.cs | 26 +- src/Ryujinx.Graphics.OpenGL/VertexArray.cs | 12 +- src/Ryujinx.Graphics.OpenGL/Window.cs | 8 +- src/Ryujinx.Graphics.Shader/AttributeType.cs | 12 +- .../CodeGen/Glsl/Declarations.cs | 12 +- .../Glsl/Instructions/InstGenMemory.cs | 8 +- .../CodeGen/Spirv/Declarations.cs | 6 +- .../CodeGen/Spirv/Instructions.cs | 14 +- .../Decoders/Decoder.cs | 2 +- src/Ryujinx.Graphics.Shader/InputTopology.cs | 22 +- .../Instructions/InstEmitMove.cs | 2 +- .../Instructions/InstEmitSurface.cs | 8 +- .../Instructions/InstEmitTexture.cs | 54 +- src/Ryujinx.Graphics.Shader/OutputTopology.cs | 5 +- src/Ryujinx.Graphics.Shader/SamplerType.cs | 227 +- src/Ryujinx.Graphics.Shader/ShaderStage.cs | 36 +- src/Ryujinx.Graphics.Shader/TessPatchType.cs | 4 +- src/Ryujinx.Graphics.Shader/TessSpacing.cs | 4 +- .../Translation/EmitterContext.cs | 4 +- .../Optimizations/BindlessElimination.cs | 4 +- .../Translation/ResourceManager.cs | 14 +- .../Translation/ShaderDefinitions.cs | 6 +- .../Translation/Transforms/TexturePass.cs | 12 +- src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 9 +- .../FormatCapabilities.cs | 6 +- .../FramebufferParams.cs | 18 +- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 14 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 +- .../PipelineConverter.cs | 16 +- src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 2 +- src/Ryujinx.Graphics.Vulkan/TextureCopy.cs | 2 +- src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 8 +- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 22 +- .../VulkanException.cs | 17 +- src/Ryujinx.Graphics.Vulkan/Window.cs | 2 +- src/Ryujinx.HLE/HOS/Horizon.cs | 4 +- src/Ryujinx.HLE/HOS/HorizonFsClient.cs | 12 +- .../Kernel/Process/CapabilityExtensions.cs | 17 +- .../Kernel/Process/KProcessCapabilities.cs | 2 +- .../HOS/Services/Hid/IHidServer.cs | 4 +- .../HOS/Services/Spl/IGeneralInterface.cs | 2 +- src/Ryujinx.HLE/MemoryConfiguration.cs | 42 +- src/Ryujinx.HLE/Switch.cs | 2 +- .../Bcat/Ipc/ServiceCreator.cs | 4 +- .../DeliveryCacheDirectoryService.cs | 6 +- .../DeliveryCacheFileService.cs | 8 +- .../DeliveryCacheStorageService.cs | 6 +- src/Ryujinx.Horizon/LibHacResultExtensions.cs | 4 +- .../Tracking/MultiRegionHandle.cs | 5 +- .../MultiRegionTrackingTests.cs | 4 +- src/Ryujinx/Headless/Options.cs | 4 +- src/Ryujinx/Program.cs | 8 +- .../Configuration/ConfigurationState.Model.cs | 4 +- .../Systems/Configuration/System/Language.cs | 13 +- .../Systems/Configuration/System/Region.cs | 13 +- .../XCITrimmerFileStatusDetailConverter.cs | 8 +- .../XCITrimmerOperationOutcomeHelper.cs | 39 +- .../UI/ViewModels/AboutWindowViewModel.cs | 17 +- .../UI/ViewModels/AmiiboWindowViewModel.cs | 49 +- .../UI/ViewModels/DlcSelectViewModel.cs | 8 +- .../DownloadableContentManagerViewModel.cs | 23 +- .../Input/ControllerInputViewModel.cs | 27 +- .../UI/ViewModels/Input/InputViewModel.cs | 37 +- .../Input/KeyboardInputViewModel.cs | 23 +- .../UI/ViewModels/Input/LedInputViewModel.cs | 19 +- .../ViewModels/Input/MotionInputViewModel.cs | 24 +- .../ViewModels/Input/RumbleInputViewModel.cs | 6 +- .../UI/ViewModels/LdnGamesListViewModel.cs | 22 +- .../UI/ViewModels/MainWindowViewModel.cs | 437 +- .../UI/ViewModels/ModManagerViewModel.cs | 19 +- .../ProfileSelectorDialogViewModel.cs | 6 +- .../UI/ViewModels/SettingsHacksViewModel.cs | 8 +- .../UI/ViewModels/SettingsViewModel.cs | 156 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 15 +- .../UserFirmwareAvatarSelectorViewModel.cs | 25 +- .../UserProfileImageSelectorViewModel.cs | 3 +- .../UI/ViewModels/UserSaveManagerViewModel.cs | 20 +- .../UI/ViewModels/XciTrimmerViewModel.cs | 15 +- src/Ryujinx/UI/Views/Dialog/AboutView.axaml | 8 + .../Utilities/StorageProviderExtensions.cs | 33 +- 171 files changed, 6011 insertions(+), 6335 deletions(-) delete mode 100644 src/ARMeilleure/Instructions/SoftFallback.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Aes.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Count.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Crc32.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Saturation.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Sha1.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Sha256.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.ShrImm64.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Table.cs create mode 100644 src/ARMeilleure/Instructions/SoftFallback/SoftFallback.cs delete mode 100644 src/ARMeilleure/Instructions/SoftFloat.cs create mode 100644 src/ARMeilleure/Instructions/SoftFloat/SoftFloat.cs create mode 100644 src/ARMeilleure/Instructions/SoftFloat/SoftFloat16.cs create mode 100644 src/ARMeilleure/Instructions/SoftFloat/SoftFloat16_32.cs create mode 100644 src/ARMeilleure/Instructions/SoftFloat/SoftFloat16_64.cs create mode 100644 src/ARMeilleure/Instructions/SoftFloat/SoftFloat32.cs create mode 100644 src/ARMeilleure/Instructions/SoftFloat/SoftFloat32_16.cs create mode 100644 src/ARMeilleure/Instructions/SoftFloat/SoftFloat64.cs create mode 100644 src/ARMeilleure/Instructions/SoftFloat/SoftFloat64_16.cs diff --git a/COMPILING.md b/COMPILING.md index 238c1ade8..edfb35ac4 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -5,7 +5,7 @@ If you wish to build the emulator yourself, follow these steps: ### Step 1 -Install the [.NET 9.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/9.0). +Install the [.NET 10.0 (or higher) SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0). Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json). ### Step 2 diff --git a/Directory.Build.props b/Directory.Build.props index d7a2ac1f2..a4df830a3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - net9.0 - latest + net10.0 + preview diff --git a/Ryujinx.sln b/Ryujinx.sln index b25844245..24def42a3 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -85,6 +85,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .github\workflows\build.yml = .github\workflows\build.yml .github\workflows\canary.yml = .github\workflows\canary.yml Directory.Packages.props = Directory.Packages.props + Directory.Build.props = Directory.Build.props .github\workflows\release.yml = .github\workflows\release.yml nuget.config = nuget.config EndProjectSection diff --git a/global.json b/global.json index cdbb589ed..512142d2b 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "10.0.100", "rollForward": "latestFeature" } } diff --git a/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs b/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs index 5db898591..755e9573a 100644 --- a/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs +++ b/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs @@ -25,9 +25,9 @@ namespace ARMeilleure.CodeGen.Arm64 static class ComparisonArm64Extensions { - public static ArmCondition ToArmCondition(this Comparison comp) + extension(Comparison comparison) { - return comp switch + public ArmCondition Arm => comparison switch { #pragma warning disable IDE0055 // Disable formatting Comparison.Equal => ArmCondition.Eq, @@ -42,7 +42,7 @@ namespace ARMeilleure.CodeGen.Arm64 Comparison.LessUI => ArmCondition.LtUn, #pragma warning restore IDE0055 - _ => throw new ArgumentException(null, nameof(comp)), + _ => throw new ArgumentException(null, nameof(comparison)) }; } } diff --git a/src/ARMeilleure/CodeGen/Arm64/Assembler.cs b/src/ARMeilleure/CodeGen/Arm64/Assembler.cs index 0d493426b..ee696c5f2 100644 --- a/src/ARMeilleure/CodeGen/Arm64/Assembler.cs +++ b/src/ARMeilleure/CodeGen/Arm64/Assembler.cs @@ -181,10 +181,10 @@ namespace ARMeilleure.CodeGen.Arm64 public void Fmov(Operand rd, Operand rn, bool topHalf) { - Debug.Assert(rd.Type.IsInteger() != rn.Type.IsInteger()); + 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 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; @@ -411,7 +411,7 @@ namespace ARMeilleure.CodeGen.Arm64 public void Mov(Operand rd, Operand rn) { - if (rd.Type.IsInteger()) + if (rd.Type.IsInteger) { Orr(rd, Factory.Register(ZrRegister, RegisterType.Integer, rd.Type), rn); } @@ -973,7 +973,7 @@ namespace ARMeilleure.CodeGen.Arm64 uint instruction; int scale; - if (type.IsInteger()) + if (type.IsInteger) { instruction = intInst; @@ -1009,7 +1009,7 @@ namespace ARMeilleure.CodeGen.Arm64 { uint instruction; - if (type.IsInteger()) + if (type.IsInteger) { instruction = intInst; diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs index fbf4c1eb4..320e86dc2 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs @@ -250,7 +250,7 @@ namespace ARMeilleure.CodeGen.Arm64 // ValidateBinOp(dest, src1, src2); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Add(dest, src1, src2); } @@ -268,7 +268,7 @@ namespace ARMeilleure.CodeGen.Arm64 ValidateBinOp(dest, src1, src2); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.And(dest, src1, src2); } @@ -281,7 +281,7 @@ namespace ARMeilleure.CodeGen.Arm64 ValidateBinOp(dest, src1, src2); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Eor(dest, src1, src2); } @@ -298,7 +298,7 @@ namespace ARMeilleure.CodeGen.Arm64 ValidateUnOp(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Mvn(dest, source); } @@ -311,7 +311,7 @@ namespace ARMeilleure.CodeGen.Arm64 ValidateBinOp(dest, src1, src2); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Orr(dest, src1, src2); } @@ -322,7 +322,7 @@ namespace ARMeilleure.CodeGen.Arm64 Debug.Assert(comp.Kind == OperandKind.Constant); - ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition(); + ArmCondition cond = ((Comparison)comp.AsInt32()).Arm; GenerateCompareCommon(context, operation); @@ -336,7 +336,7 @@ namespace ARMeilleure.CodeGen.Arm64 ValidateUnOp(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Rev(dest, source); } @@ -354,7 +354,7 @@ namespace ARMeilleure.CodeGen.Arm64 Debug.Assert(dest.Type == OperandType.I32); Debug.Assert(comp.Kind == OperandKind.Constant); - ArmCondition cond = ((Comparison)comp.AsInt32()).ToArmCondition(); + ArmCondition cond = ((Comparison)comp.AsInt32()).Arm; GenerateCompareCommon(context, operation); @@ -428,7 +428,7 @@ namespace ARMeilleure.CodeGen.Arm64 EnsureSameType(src1, src2); - Debug.Assert(src1.Type.IsInteger()); + Debug.Assert(src1.Type.IsInteger); context.Assembler.Cmp(src1, src2); } @@ -442,7 +442,7 @@ namespace ARMeilleure.CodeGen.Arm64 EnsureSameType(dest, src2, src3); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); Debug.Assert(src1.Type == OperandType.I32); context.Assembler.Cmp(src1, Const(src1.Type, 0)); @@ -468,7 +468,7 @@ namespace ARMeilleure.CodeGen.Arm64 Debug.Assert(dest.Type != source.Type); Debug.Assert(source.Type != OperandType.V128); - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { context.Assembler.ScvtfScalar(dest, source); } @@ -485,7 +485,7 @@ namespace ARMeilleure.CodeGen.Arm64 Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64); Debug.Assert(dest.Type != source.Type); - Debug.Assert(source.Type.IsInteger()); + Debug.Assert(source.Type.IsInteger); context.Assembler.UcvtfScalar(dest, source); } @@ -497,7 +497,7 @@ namespace ARMeilleure.CodeGen.Arm64 EnsureSameType(dest, source); - Debug.Assert(dest.Type.IsInteger() || source.Kind != OperandKind.Constant); + 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) @@ -529,7 +529,7 @@ namespace ARMeilleure.CodeGen.Arm64 EnsureSameType(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Clz(dest, source); } @@ -542,7 +542,7 @@ namespace ARMeilleure.CodeGen.Arm64 ValidateBinOp(dest, dividend, divisor); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Sdiv(dest, dividend, divisor); } @@ -576,7 +576,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand value = operation.Destination; Operand address = operation.GetSource(0); - Debug.Assert(value.Type.IsInteger()); + Debug.Assert(value.Type.IsInteger); context.Assembler.LdrhRiUn(value, address, 0); } @@ -586,7 +586,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand value = operation.Destination; Operand address = operation.GetSource(0); - Debug.Assert(value.Type.IsInteger()); + Debug.Assert(value.Type.IsInteger); context.Assembler.LdrbRiUn(value, address, 0); } @@ -604,7 +604,7 @@ namespace ARMeilleure.CodeGen.Arm64 EnsureSameType(dest, src1, src2); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Mul(dest, src1, src2); } @@ -647,7 +647,7 @@ namespace ARMeilleure.CodeGen.Arm64 ValidateUnOp(dest, source); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Neg(dest, source); } @@ -732,7 +732,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Sxth(dest, source); } @@ -742,7 +742,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Sxtw(dest, source); } @@ -752,7 +752,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Sxtb(dest, source); } @@ -823,7 +823,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand value = operation.GetSource(1); Operand address = operation.GetSource(0); - Debug.Assert(value.Type.IsInteger()); + Debug.Assert(value.Type.IsInteger); context.Assembler.StrhRiUn(value, address, 0); } @@ -833,7 +833,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand value = operation.GetSource(1); Operand address = operation.GetSource(0); - Debug.Assert(value.Type.IsInteger()); + Debug.Assert(value.Type.IsInteger); context.Assembler.StrbRiUn(value, address, 0); } @@ -858,7 +858,7 @@ namespace ARMeilleure.CodeGen.Arm64 // ValidateBinOp(dest, src1, src2); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Sub(dest, src1, src2); } @@ -882,7 +882,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (dest != default) { - Debug.Assert(!dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger && source.Type.IsInteger); OperandType destType = source.Type == OperandType.I64 ? OperandType.FP64 : OperandType.FP32; @@ -901,9 +901,9 @@ namespace ARMeilleure.CodeGen.Arm64 byte index = src2.AsByte(); - Debug.Assert(index < OperandType.V128.GetSizeInBytes() / dest.Type.GetSizeInBytes()); + Debug.Assert(index < OperandType.V128.ByteSize / dest.Type.ByteSize); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Umov(dest, src1, index, dest.Type == OperandType.I64 ? 3 : 2); } @@ -959,7 +959,7 @@ namespace ARMeilleure.CodeGen.Arm64 byte index = src3.AsByte(); - if (src2.Type.IsInteger()) + if (src2.Type.IsInteger) { context.Assembler.Ins(dest, src2, index, src2.Type == OperandType.I64 ? 3 : 2); } @@ -1007,7 +1007,7 @@ namespace ARMeilleure.CodeGen.Arm64 { Operand dest = operation.Destination; - Debug.Assert(!dest.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger); context.Assembler.CmeqVector(dest, dest, dest, 2); } @@ -1016,7 +1016,7 @@ namespace ARMeilleure.CodeGen.Arm64 { Operand dest = operation.Destination; - Debug.Assert(!dest.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger); context.Assembler.EorVector(dest, dest, dest); } @@ -1046,7 +1046,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Uxth(dest, source); } @@ -1056,7 +1056,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + 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) @@ -1072,7 +1072,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Uxtb(dest, source); } @@ -1169,7 +1169,7 @@ namespace ARMeilleure.CodeGen.Arm64 context.Assembler.StrRiPre(Register(reg, type), Register(SpRegister), -calleeSaveRegionSize); } - offset += type.GetSizeInBytes(); + offset += type.ByteSize; } while (mask != 0) @@ -1195,7 +1195,7 @@ namespace ARMeilleure.CodeGen.Arm64 context.Assembler.StpRiPre(Register(reg, type), Register(reg2, type), Register(SpRegister), -calleeSaveRegionSize); } - offset += type.GetSizeInBytes() * 2; + offset += type.ByteSize * 2; } } @@ -1273,7 +1273,7 @@ namespace ARMeilleure.CodeGen.Arm64 mask &= ~(1 << reg2); - offset -= type.GetSizeInBytes() * 2; + offset -= type.ByteSize * 2; if (offset != 0) { @@ -1286,7 +1286,7 @@ namespace ARMeilleure.CodeGen.Arm64 } else { - offset -= type.GetSizeInBytes(); + offset -= type.ByteSize; if (offset != 0) { @@ -1435,12 +1435,12 @@ namespace ARMeilleure.CodeGen.Arm64 OperandType valueType = GetMemOpValueType(currentOp); - if (valueType != GetMemOpValueType(nextOp) || op1Offset + valueType.GetSizeInBytes() != op2Offset) + if (valueType != GetMemOpValueType(nextOp) || op1Offset + valueType.ByteSize != op2Offset) { return false; } - if (!CodeGenCommon.ConstFitsOnSImm7(op1Offset, valueType.GetSizeInBytesLog2())) + if (!CodeGenCommon.ConstFitsOnSImm7(op1Offset, valueType.ByteSizeLog2)) { return false; } @@ -1549,7 +1549,7 @@ namespace ARMeilleure.CodeGen.Arm64 // EnsureSameReg (dest, src1); EnsureSameType(dest, src1); - Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32); + Debug.Assert(dest.Type.IsInteger && src2.Type == OperandType.I32); } private static void EnsureSameReg(Operand op1, Operand op2) diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs index 390dc5b2e..e7871289b 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs @@ -462,7 +462,7 @@ namespace ARMeilleure.CodeGen.Arm64 { instruction |= (sz << 22); - if (rd.Type.IsInteger()) + if (rd.Type.IsInteger) { context.Assembler.WriteInstructionAuto(instruction, rd, rn); } @@ -490,7 +490,7 @@ namespace ARMeilleure.CodeGen.Arm64 instruction |= (sz << 22); instruction |= (64 - fBits) << 10; - if (rd.Type.IsInteger()) + if (rd.Type.IsInteger) { Debug.Assert(rd.Type != OperandType.I32 || fBits <= 32); diff --git a/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs b/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs index 76a231d6c..d52aba162 100644 --- a/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs +++ b/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs @@ -112,7 +112,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (src1.Kind == OperandKind.Constant) { - if (!src1.Type.IsInteger()) + if (!src1.Type.IsInteger) { // Handle non-integer types (FP32, FP64 and V128). // For instructions without an immediate operand, we do the following: @@ -161,7 +161,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (src2.Kind == OperandKind.Constant) { - if (!src2.Type.IsInteger()) + if (!src2.Type.IsInteger) { src2 = AddFloatConstantCopy(constants, nodes, node, src2); @@ -191,7 +191,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (src.Kind == OperandKind.Constant) { - if (!src.Type.IsInteger()) + if (!src.Type.IsInteger) { src = AddFloatConstantCopy(constants, nodes, node, src); @@ -282,7 +282,7 @@ namespace ARMeilleure.CodeGen.Arm64 bool passOnReg; - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { passOnReg = intCount < intMax; } @@ -309,7 +309,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (passOnReg) { - Operand argReg = source.Type.IsInteger() + Operand argReg = source.Type.IsInteger ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); @@ -327,7 +327,7 @@ namespace ARMeilleure.CodeGen.Arm64 InsertConstantRegCopies(constants, nodes, nodes.AddBefore(node, spillOp)); - stackOffset += source.Type.GetSizeInBytes(); + stackOffset += source.Type.ByteSize; } } @@ -345,7 +345,7 @@ namespace ARMeilleure.CodeGen.Arm64 } else { - Operand retReg = dest.Type.IsInteger() + Operand retReg = dest.Type.IsInteger ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type) : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type); @@ -385,7 +385,7 @@ namespace ARMeilleure.CodeGen.Arm64 bool passOnReg; - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { passOnReg = intCount + 1 < intMax; } @@ -408,7 +408,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (passOnReg) { - Operand argReg = source.Type.IsInteger() + Operand argReg = source.Type.IsInteger ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); @@ -521,7 +521,7 @@ namespace ARMeilleure.CodeGen.Arm64 } else { - Operand retReg = source.Type.IsInteger() + Operand retReg = source.Type.IsInteger ? Gpr(CallingConvention.GetIntReturnRegister(), source.Type) : Xmm(CallingConvention.GetVecReturnRegister(), source.Type); @@ -551,7 +551,7 @@ namespace ARMeilleure.CodeGen.Arm64 { OperandType argType = cctx.FuncArgTypes[cIndex]; - if (argType.IsInteger()) + if (argType.IsInteger) { intCount++; } @@ -567,7 +567,7 @@ namespace ARMeilleure.CodeGen.Arm64 bool passOnReg; - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { passOnReg = intCount < CallingConvention.GetArgumentsOnRegsCount(); } @@ -606,7 +606,7 @@ namespace ARMeilleure.CodeGen.Arm64 { Operand pArg = Local(dest.Type); - Operand argReg = dest.Type.IsInteger() + Operand argReg = dest.Type.IsInteger ? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type) : Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type); diff --git a/src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs b/src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs index 5f0e37721..4a9f6a834 100644 --- a/src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs +++ b/src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs @@ -51,7 +51,7 @@ namespace ARMeilleure.CodeGen.Optimizations if (trueSucc == block.ListNext) { Comparison comp = (Comparison)branchOp.GetSource(2).AsInt32(); - Comparison compInv = comp.Invert(); + Comparison compInv = comp.Inverse; branchOp.SetSource(2, Const((int)compInv)); diff --git a/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs b/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs index cbc6ab784..c1de22757 100644 --- a/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs +++ b/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs @@ -161,7 +161,7 @@ namespace ARMeilleure.CodeGen.Optimizations } else if (otherCompType == Comparison.Equal) { - propCompType = compType.Invert(); + propCompType = compType.Inverse; } else { diff --git a/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs b/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs index 53a7f3ede..a80b4adad 100644 --- a/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs +++ b/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs @@ -105,7 +105,7 @@ namespace ARMeilleure.CodeGen.Optimizations Operand x = operation.GetSource(0); Operand y = operation.GetSource(1); - if (x == y && x.Type.IsInteger()) + if (x == y && x.Type.IsInteger) { operation.TurnIntoCopy(Const(x.Type, 0)); } @@ -161,7 +161,7 @@ namespace ARMeilleure.CodeGen.Optimizations private static bool IsConstEqual(Operand operand, ulong comparand) { - if (operand.Kind != OperandKind.Constant || !operand.Type.IsInteger()) + if (operand.Kind != OperandKind.Constant || !operand.Type.IsInteger) { return false; } diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs index 8b135afab..574de4cd6 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs @@ -98,7 +98,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { OperandType type = types[copyDest]; - type = type.IsInteger() ? OperandType.I64 : OperandType.V128; + type = type.IsInteger ? OperandType.I64 : OperandType.V128; EmitXorSwap(sequence, GetRegister(copyDest, type), GetRegister(copySource, type)); diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs index 5f1d6ce89..1e9aee5fd 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs @@ -178,7 +178,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } else if (dest.Kind == OperandKind.Register) { - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { intFixedRegisters |= 1 << dest.GetRegister().Index; } @@ -236,7 +236,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { Register reg = info.Register.GetRegister(); - if (local.Type.IsInteger()) + if (local.Type.IsInteger) { intLocalFreeRegisters |= 1 << reg.Index; } @@ -254,7 +254,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (temp == default || info.Sequence != sequence) { - temp = local.Type.IsInteger() + temp = local.Type.IsInteger ? GetSpillTemp(local, intSpillTempRegisters, ref intLocalUse) : GetSpillTemp(local, vecSpillTempRegisters, ref vecLocalUse); @@ -335,7 +335,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (info.UsesAllocated == 0) { - int mask = dest.Type.IsInteger() + int mask = dest.Type.IsInteger ? intLocalFreeRegisters : vecLocalFreeRegisters; @@ -343,9 +343,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { int selectedReg = BitOperations.TrailingZeroCount(mask); - info.Register = Register(selectedReg, info.Type.ToRegisterType(), info.Type); + info.Register = Register(selectedReg, info.Type.Register, info.Type); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { intLocalFreeRegisters &= ~(1 << selectedReg); intUsedRegisters |= 1 << selectedReg; @@ -359,7 +359,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators else { info.Register = default; - info.SpillOffset = Const(stackAlloc.Allocate(dest.Type.GetSizeInBytes())); + info.SpillOffset = Const(stackAlloc.Allocate(dest.Type.ByteSize)); } } @@ -377,7 +377,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (temp == default || info.Sequence != sequence) { - temp = dest.Type.IsInteger() + temp = dest.Type.IsInteger ? GetSpillTemp(dest, intSpillTempRegisters, ref intLocalAsg) : GetSpillTemp(dest, vecSpillTempRegisters, ref vecLocalAsg); @@ -443,7 +443,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators useMask |= 1 << selectedReg; - return Register(selectedReg, local.Type.ToRegisterType(), local.Type); + return Register(selectedReg, local.Type.Register, local.Type); } private static int UsesCount(Operand local) diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs index 92fedf7bf..94883b39b 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs @@ -208,7 +208,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private bool TryAllocateRegWithoutSpill(AllocationContext context, LiveInterval current, int cIndex, int registersCount) { - RegisterType regType = current.Local.Type.ToRegisterType(); + RegisterType regType = current.Local.Type.Register; Span freePositions = stackalloc int[registersCount]; @@ -318,7 +318,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private void AllocateRegWithSpill(AllocationContext context, LiveInterval current, int cIndex, int registersCount) { - RegisterType regType = current.Local.Type.ToRegisterType(); + RegisterType regType = current.Local.Type.Register; Span usePositions = stackalloc int[registersCount]; Span blockedPositions = stackalloc int[registersCount]; diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs index 13995bc8d..b89034609 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs @@ -10,7 +10,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public int Allocate(OperandType type) { - return Allocate(type.GetSizeInBytes()); + return Allocate(type.ByteSize); } public int Allocate(int sizeInBytes) diff --git a/src/ARMeilleure/CodeGen/X86/Assembler.cs b/src/ARMeilleure/CodeGen/X86/Assembler.cs index 5a8312806..e12866990 100644 --- a/src/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/src/ARMeilleure/CodeGen/X86/Assembler.cs @@ -385,7 +385,7 @@ namespace ARMeilleure.CodeGen.X86 { ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Movd]; - if (source.Type.IsInteger() || source.Kind == OperandKind.Memory) + if (source.Type.IsInteger || source.Kind == OperandKind.Memory) { WriteOpCode(dest, default, source, OperandType.None, info.Flags, info.OpRRM, rrm: true); } @@ -416,11 +416,11 @@ namespace ARMeilleure.CodeGen.X86 InstructionFlags flags = info.Flags | InstructionFlags.RexW; - if (source.Type.IsInteger() || source.Kind == OperandKind.Memory) + if (source.Type.IsInteger || source.Kind == OperandKind.Memory) { WriteOpCode(dest, default, source, OperandType.None, flags, info.OpRRM, rrm: true); } - else if (dest.Type.IsInteger() || dest.Kind == OperandKind.Memory) + else if (dest.Type.IsInteger || dest.Kind == OperandKind.Memory) { WriteOpCode(dest, default, source, OperandType.None, flags, info.OpRMR); } diff --git a/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs index 86acea4a8..ed425f476 100644 --- a/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -289,7 +289,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameType(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Popcnt(dest, source, dest.Type); @@ -303,7 +303,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameType(dest, source); - Debug.Assert(!dest.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger); context.Assembler.WriteInstruction(info.Inst, dest, source); @@ -315,7 +315,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && !source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && !source.Type.IsInteger); if (operation.Intrinsic == Intrinsic.X86Cvtsi2si) { @@ -349,8 +349,8 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameReg(dest, src1); } - Debug.Assert(!dest.Type.IsInteger()); - Debug.Assert(!src2.Type.IsInteger() || src2.Kind == OperandKind.Constant); + Debug.Assert(!dest.Type.IsInteger); + Debug.Assert(!src2.Type.IsInteger || src2.Kind == OperandKind.Constant); context.Assembler.WriteInstruction(info.Inst, dest, src1, src2); @@ -370,7 +370,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameReg(dest, src1); } - Debug.Assert(!dest.Type.IsInteger() && src2.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger && src2.Type.IsInteger); context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src2.Type); @@ -385,7 +385,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameReg(dest, src1); - Debug.Assert(dest.Type.IsInteger() && src1.Type.IsInteger() && src2.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && src1.Type.IsInteger && src2.Type.IsInteger); context.Assembler.WriteInstruction(info.Inst, dest, src2, dest.Type); @@ -405,7 +405,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameReg(dest, src1); } - Debug.Assert(!dest.Type.IsInteger() && src2.Kind == OperandKind.Constant); + Debug.Assert(!dest.Type.IsInteger && src2.Kind == OperandKind.Constant); context.Assembler.WriteInstruction(info.Inst, dest, src1, src2.AsByte()); @@ -421,7 +421,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameType(dest, src1, src2, src3); - Debug.Assert(!dest.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger); if (info.Inst == X86Instruction.Blendvpd && HardwareCapabilities.SupportsVexEncoding) { @@ -461,7 +461,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameReg(dest, src1); } - Debug.Assert(!dest.Type.IsInteger() && src3.Kind == OperandKind.Constant); + Debug.Assert(!dest.Type.IsInteger && src3.Kind == OperandKind.Constant); context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src3.AsByte()); @@ -512,7 +512,7 @@ namespace ARMeilleure.CodeGen.X86 Operand src1 = operation.GetSource(0); Operand src2 = operation.GetSource(1); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { // If Destination and Source 1 Operands are the same, perform a standard add as there are no benefits to using LEA. if (dest.Kind == src1.Kind && dest.Value == src1.Value) @@ -567,7 +567,7 @@ namespace ARMeilleure.CodeGen.X86 ValidateBinOp(dest, src1, src2); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); // Note: GenerateCompareCommon makes the assumption that BitwiseAnd will emit only a single `and` // instruction. @@ -582,7 +582,7 @@ namespace ARMeilleure.CodeGen.X86 ValidateBinOp(dest, src1, src2); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Xor(dest, src2, dest.Type); } @@ -599,7 +599,7 @@ namespace ARMeilleure.CodeGen.X86 ValidateUnOp(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Not(dest); } @@ -612,7 +612,7 @@ namespace ARMeilleure.CodeGen.X86 ValidateBinOp(dest, src1, src2); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Or(dest, src2, dest.Type); } @@ -623,7 +623,7 @@ namespace ARMeilleure.CodeGen.X86 Debug.Assert(comp.Kind == OperandKind.Constant); - X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition(); + X86Condition cond = ((Comparison)comp.AsInt32()).X86; GenerateCompareCommon(context, operation); @@ -637,7 +637,7 @@ namespace ARMeilleure.CodeGen.X86 ValidateUnOp(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Bswap(dest); } @@ -661,7 +661,7 @@ namespace ARMeilleure.CodeGen.X86 Debug.Assert(dest.Type == OperandType.I32); Debug.Assert(comp.Kind == OperandKind.Constant); - X86Condition cond = ((Comparison)comp.AsInt32()).ToX86Condition(); + X86Condition cond = ((Comparison)comp.AsInt32()).X86; GenerateCompareCommon(context, operation); @@ -676,7 +676,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameType(src1, src2); - Debug.Assert(src1.Type.IsInteger()); + Debug.Assert(src1.Type.IsInteger); if (src2.Kind == OperandKind.Constant && src2.Value == 0) { @@ -766,7 +766,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameReg(dest, src3); EnsureSameType(dest, src2, src3); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); Debug.Assert(src1.Type == OperandType.I32); context.Assembler.Test(src1, src1, src1.Type); @@ -792,9 +792,9 @@ namespace ARMeilleure.CodeGen.X86 if (dest.Type == OperandType.FP32) { - Debug.Assert(source.Type.IsInteger() || source.Type == OperandType.FP64); + Debug.Assert(source.Type.IsInteger || source.Type == OperandType.FP64); - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { context.Assembler.Xorps(dest, dest, dest); context.Assembler.Cvtsi2ss(dest, dest, source, source.Type); @@ -808,9 +808,9 @@ namespace ARMeilleure.CodeGen.X86 } else /* if (dest.Type == OperandType.FP64) */ { - Debug.Assert(source.Type.IsInteger() || source.Type == OperandType.FP32); + Debug.Assert(source.Type.IsInteger || source.Type == OperandType.FP32); - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { context.Assembler.Xorps(dest, dest, dest); context.Assembler.Cvtsi2sd(dest, dest, source, source.Type); @@ -831,7 +831,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameType(dest, source); - Debug.Assert(dest.Type.IsInteger() || source.Kind != OperandKind.Constant); + 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) @@ -845,7 +845,7 @@ namespace ARMeilleure.CodeGen.X86 // Assemble "mov reg, 0" as "xor reg, reg" as the later is more efficient. context.Assembler.Xor(dest, dest, OperandType.I32); } - else if (dest.Type.IsInteger()) + else if (dest.Type.IsInteger) { context.Assembler.Mov(dest, source, dest.Type); } @@ -862,7 +862,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameType(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Bsr(dest, source, dest.Type); @@ -894,12 +894,12 @@ namespace ARMeilleure.CodeGen.X86 Operand dividend = operation.GetSource(0); Operand divisor = operation.GetSource(1); - if (!dest.Type.IsInteger()) + if (!dest.Type.IsInteger) { ValidateBinOp(dest, dividend, divisor); } - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { divisor = operation.GetSource(2); @@ -932,7 +932,7 @@ namespace ARMeilleure.CodeGen.X86 Operand rdx = Register(X86Register.Rdx); - Debug.Assert(divisor.Type.IsInteger()); + Debug.Assert(divisor.Type.IsInteger); context.Assembler.Xor(rdx, rdx, OperandType.I32); context.Assembler.Div(divisor); @@ -967,7 +967,7 @@ namespace ARMeilleure.CodeGen.X86 Operand value = operation.Destination; Operand address = Memory(operation.GetSource(0), value.Type); - Debug.Assert(value.Type.IsInteger()); + Debug.Assert(value.Type.IsInteger); context.Assembler.Movzx16(value, address, value.Type); } @@ -977,7 +977,7 @@ namespace ARMeilleure.CodeGen.X86 Operand value = operation.Destination; Operand address = Memory(operation.GetSource(0), value.Type); - Debug.Assert(value.Type.IsInteger()); + Debug.Assert(value.Type.IsInteger); context.Assembler.Movzx8(value, address, value.Type); } @@ -1000,7 +1000,7 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameType(dest, src1, src2); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { if (src2.Kind == OperandKind.Constant) { @@ -1046,7 +1046,7 @@ namespace ARMeilleure.CodeGen.X86 ValidateUnOp(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger); context.Assembler.Neg(dest); } @@ -1107,7 +1107,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Movsx16(dest, source, dest.Type); } @@ -1117,7 +1117,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Movsx32(dest, source, dest.Type); } @@ -1127,7 +1127,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Movsx8(dest, source, dest.Type); } @@ -1187,7 +1187,7 @@ namespace ARMeilleure.CodeGen.X86 Operand value = operation.GetSource(1); Operand address = Memory(operation.GetSource(0), value.Type); - Debug.Assert(value.Type.IsInteger()); + Debug.Assert(value.Type.IsInteger); context.Assembler.Mov16(address, value); } @@ -1197,7 +1197,7 @@ namespace ARMeilleure.CodeGen.X86 Operand value = operation.GetSource(1); Operand address = Memory(operation.GetSource(0), value.Type); - Debug.Assert(value.Type.IsInteger()); + Debug.Assert(value.Type.IsInteger); context.Assembler.Mov8(address, value); } @@ -1210,7 +1210,7 @@ namespace ARMeilleure.CodeGen.X86 ValidateBinOp(dest, src1, src2); - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { context.Assembler.Sub(dest, src2, dest.Type); } @@ -1236,7 +1236,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(!dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger && source.Type.IsInteger); if (source.Type == OperandType.I32) { @@ -1259,7 +1259,7 @@ namespace ARMeilleure.CodeGen.X86 byte index = src2.AsByte(); - Debug.Assert(index < OperandType.V128.GetSizeInBytes() / dest.Type.GetSizeInBytes()); + Debug.Assert(index < OperandType.V128.ByteSize / dest.Type.ByteSize); if (dest.Type == OperandType.I32) { @@ -1541,7 +1541,7 @@ namespace ARMeilleure.CodeGen.X86 { Operand dest = operation.Destination; - Debug.Assert(!dest.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger); context.Assembler.Pcmpeqw(dest, dest, dest); } @@ -1550,7 +1550,7 @@ namespace ARMeilleure.CodeGen.X86 { Operand dest = operation.Destination; - Debug.Assert(!dest.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger); context.Assembler.Xorps(dest, dest, dest); } @@ -1580,7 +1580,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Movzx16(dest, source, OperandType.I32); } @@ -1590,7 +1590,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + 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) @@ -1606,7 +1606,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = operation.Destination; Operand source = operation.GetSource(0); - Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger && source.Type.IsInteger); context.Assembler.Movzx8(dest, source, OperandType.I32); } @@ -1713,12 +1713,12 @@ namespace ARMeilleure.CodeGen.X86 EnsureSameReg(dest, src1); EnsureSameType(dest, src1); - Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32); + Debug.Assert(dest.Type.IsInteger && src2.Type == OperandType.I32); } private static void EnsureSameReg(Operand op1, Operand op2) { - if (!op1.Type.IsInteger() && HardwareCapabilities.SupportsVexEncoding) + if (!op1.Type.IsInteger && HardwareCapabilities.SupportsVexEncoding) { return; } diff --git a/src/ARMeilleure/CodeGen/X86/PreAllocator.cs b/src/ARMeilleure/CodeGen/X86/PreAllocator.cs index 6b93efdfb..d3bc6be6a 100644 --- a/src/ARMeilleure/CodeGen/X86/PreAllocator.cs +++ b/src/ARMeilleure/CodeGen/X86/PreAllocator.cs @@ -86,7 +86,7 @@ namespace ARMeilleure.CodeGen.X86 break; case Instruction.Negate: - if (!node.GetSource(0).Type.IsInteger()) + if (!node.GetSource(0).Type.IsInteger) { GenerateNegate(block.Operations, node); } @@ -159,7 +159,7 @@ namespace ARMeilleure.CodeGen.X86 if (src1.Kind == OperandKind.Constant) { - if (!src1.Type.IsInteger()) + if (!src1.Type.IsInteger) { // Handle non-integer types (FP32, FP64 and V128). // For instructions without an immediate operand, we do the following: @@ -208,7 +208,7 @@ namespace ARMeilleure.CodeGen.X86 if (src2.Kind == OperandKind.Constant) { - if (!src2.Type.IsInteger()) + if (!src2.Type.IsInteger) { src2 = AddXmmCopy(nodes, node, src2); @@ -298,7 +298,7 @@ namespace ARMeilleure.CodeGen.X86 // - 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()) + if (dest.Type.IsInteger) { Operand src1 = node.GetSource(0); @@ -466,7 +466,7 @@ namespace ARMeilleure.CodeGen.X86 Operand dest = node.Destination; Operand source = node.GetSource(0); - Debug.Assert(source.Type.IsInteger(), $"Invalid source type \"{source.Type}\"."); + Debug.Assert(source.Type.IsInteger, $"Invalid source type \"{source.Type}\"."); Operation currentNode = node; @@ -654,10 +654,10 @@ namespace ARMeilleure.CodeGen.X86 switch (operation.Instruction) { case Instruction.Add: - return !HardwareCapabilities.SupportsVexEncoding && !operation.Destination.Type.IsInteger(); + return !HardwareCapabilities.SupportsVexEncoding && !operation.Destination.Type.IsInteger; case Instruction.Multiply: case Instruction.Subtract: - return !HardwareCapabilities.SupportsVexEncoding || operation.Destination.Type.IsInteger(); + return !HardwareCapabilities.SupportsVexEncoding || operation.Destination.Type.IsInteger; case Instruction.BitwiseAnd: case Instruction.BitwiseExclusiveOr: @@ -672,7 +672,7 @@ namespace ARMeilleure.CodeGen.X86 return true; case Instruction.Divide: - return !HardwareCapabilities.SupportsVexEncoding && !operation.Destination.Type.IsInteger(); + return !HardwareCapabilities.SupportsVexEncoding && !operation.Destination.Type.IsInteger; case Instruction.VectorInsert: case Instruction.VectorInsert16: diff --git a/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs b/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs index cff1c7240..368c53789 100644 --- a/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs +++ b/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs @@ -35,7 +35,7 @@ namespace ARMeilleure.CodeGen.X86 bool passOnReg; - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { passOnReg = intCount < intMax; } @@ -62,7 +62,7 @@ namespace ARMeilleure.CodeGen.X86 if (passOnReg) { - Operand argReg = source.Type.IsInteger() + Operand argReg = source.Type.IsInteger ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); @@ -80,7 +80,7 @@ namespace ARMeilleure.CodeGen.X86 InsertConstantRegCopies(nodes, nodes.AddBefore(node, spillOp)); - stackOffset += source.Type.GetSizeInBytes(); + stackOffset += source.Type.ByteSize; } } @@ -102,7 +102,7 @@ namespace ARMeilleure.CodeGen.X86 } else { - Operand retReg = dest.Type.IsInteger() + Operand retReg = dest.Type.IsInteger ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type) : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type); @@ -137,7 +137,7 @@ namespace ARMeilleure.CodeGen.X86 bool passOnReg; - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { passOnReg = intCount + 1 < intMax; } @@ -160,7 +160,7 @@ namespace ARMeilleure.CodeGen.X86 if (passOnReg) { - Operand argReg = source.Type.IsInteger() + Operand argReg = source.Type.IsInteger ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); @@ -210,7 +210,7 @@ namespace ARMeilleure.CodeGen.X86 { OperandType argType = cctx.FuncArgTypes[cIndex]; - if (argType.IsInteger()) + if (argType.IsInteger) { intCount++; } @@ -226,7 +226,7 @@ namespace ARMeilleure.CodeGen.X86 bool passOnReg; - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { passOnReg = intCount < CallingConvention.GetIntArgumentsOnRegsCount(); } @@ -265,7 +265,7 @@ namespace ARMeilleure.CodeGen.X86 { Operand pArg = Local(dest.Type); - Operand argReg = dest.Type.IsInteger() + Operand argReg = dest.Type.IsInteger ? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type) : Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type); @@ -320,7 +320,7 @@ namespace ARMeilleure.CodeGen.X86 } else { - Operand retReg = source.Type.IsInteger() + Operand retReg = source.Type.IsInteger ? Gpr(CallingConvention.GetIntReturnRegister(), source.Type) : Xmm(CallingConvention.GetVecReturnRegister(), source.Type); diff --git a/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs b/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs index 52f72ac69..6f4458d74 100644 --- a/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs +++ b/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs @@ -40,7 +40,7 @@ namespace ARMeilleure.CodeGen.X86 if (dest != default && dest.Type == OperandType.V128) { - int stackOffset = AllocateOnStack(dest.Type.GetSizeInBytes()); + int stackOffset = AllocateOnStack(dest.Type.ByteSize); arg0Reg = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64); @@ -76,7 +76,7 @@ namespace ARMeilleure.CodeGen.X86 { Operand stackAddr = Local(OperandType.I64); - int stackOffset = AllocateOnStack(source.Type.GetSizeInBytes()); + int stackOffset = AllocateOnStack(source.Type.ByteSize); nodes.AddBefore(node, Operation(Instruction.StackAlloc, stackAddr, Const(stackOffset))); @@ -96,7 +96,7 @@ namespace ARMeilleure.CodeGen.X86 int argIndex = index + retArgs; - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { argReg = Gpr(CallingConvention.GetIntArgumentRegister(argIndex), source.Type); } @@ -140,7 +140,7 @@ namespace ARMeilleure.CodeGen.X86 } else { - Operand retReg = dest.Type.IsInteger() + Operand retReg = dest.Type.IsInteger ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type) : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type); @@ -171,7 +171,7 @@ namespace ARMeilleure.CodeGen.X86 for (int index = 0; index < argsCount; index++) { Operand source = node.GetSource(1 + index); - Operand argReg = source.Type.IsInteger() + Operand argReg = source.Type.IsInteger ? Gpr(CallingConvention.GetIntArgumentRegister(index), source.Type) : Xmm(CallingConvention.GetVecArgumentRegister(index), source.Type); @@ -219,7 +219,7 @@ namespace ARMeilleure.CodeGen.X86 { Operand argReg, pArg; - if (dest.Type.IsInteger()) + if (dest.Type.IsInteger) { argReg = Gpr(CallingConvention.GetIntArgumentRegister(index), dest.Type); pArg = Local(dest.Type); @@ -283,7 +283,7 @@ namespace ARMeilleure.CodeGen.X86 Operand source = node.GetSource(0); Operand retReg; - if (source.Type.IsInteger()) + if (source.Type.IsInteger) { retReg = Gpr(CallingConvention.GetIntReturnRegister(), source.Type); } diff --git a/src/ARMeilleure/CodeGen/X86/X86Condition.cs b/src/ARMeilleure/CodeGen/X86/X86Condition.cs index 70699a207..5153599b1 100644 --- a/src/ARMeilleure/CodeGen/X86/X86Condition.cs +++ b/src/ARMeilleure/CodeGen/X86/X86Condition.cs @@ -25,9 +25,9 @@ namespace ARMeilleure.CodeGen.X86 static class ComparisonX86Extensions { - public static X86Condition ToX86Condition(this Comparison comp) + extension(Comparison comparison) { - return comp switch + public X86Condition X86 => comparison switch { #pragma warning disable IDE0055 // Disable formatting Comparison.Equal => X86Condition.Equal, @@ -42,7 +42,7 @@ namespace ARMeilleure.CodeGen.X86 Comparison.LessUI => X86Condition.Below, #pragma warning restore IDE0055 - _ => throw new ArgumentException(null, nameof(comp)), + _ => throw new ArgumentException(null, nameof(comparison)) }; } } diff --git a/src/ARMeilleure/Decoders/Condition.cs b/src/ARMeilleure/Decoders/Condition.cs index 961825a10..bffe61ad5 100644 --- a/src/ARMeilleure/Decoders/Condition.cs +++ b/src/ARMeilleure/Decoders/Condition.cs @@ -22,11 +22,11 @@ namespace ARMeilleure.Decoders static class ConditionExtensions { - public static Condition Invert(this Condition cond) + extension(Condition condition) { // Bit 0 of all conditions is basically a negation bit, so // inverting this bit has the effect of inverting the condition. - return (Condition)((int)cond ^ 1); + public Condition Inverse => (Condition)((int)condition ^ 1); } } } diff --git a/src/ARMeilleure/Instructions/InstEmitHashHelper.cs b/src/ARMeilleure/Instructions/InstEmitHashHelper.cs index 19a607dfc..92f9c9c35 100644 --- a/src/ARMeilleure/Instructions/InstEmitHashHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitHashHelper.cs @@ -16,7 +16,7 @@ namespace ARMeilleure.Instructions public static Operand EmitCrc32(ArmEmitterContext context, Operand crc, Operand value, int size, bool castagnoli) { - Debug.Assert(crc.Type.IsInteger() && value.Type.IsInteger()); + Debug.Assert(crc.Type.IsInteger && value.Type.IsInteger); Debug.Assert(size is >= 0 and < 4); Debug.Assert((size < 3) || (value.Type == OperandType.I64)); diff --git a/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs index bb7e997b2..3874d0464 100644 --- a/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs @@ -157,7 +157,7 @@ namespace ARMeilleure.Instructions context.Copy(temp, value); - if (!context.Memory.Type.IsHostMappedOrTracked()) + if (!context.Memory.Type.IsHostMappedOrTracked) { context.Branch(lblEnd); @@ -198,7 +198,7 @@ namespace ARMeilleure.Instructions SetInt(context, rt, value); - if (!context.Memory.Type.IsHostMappedOrTracked()) + if (!context.Memory.Type.IsHostMappedOrTracked) { context.Branch(lblEnd); @@ -265,7 +265,7 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(rt), value); - if (!context.Memory.Type.IsHostMappedOrTracked()) + if (!context.Memory.Type.IsHostMappedOrTracked) { context.Branch(lblEnd); @@ -312,7 +312,7 @@ namespace ARMeilleure.Instructions break; } - if (!context.Memory.Type.IsHostMappedOrTracked()) + if (!context.Memory.Type.IsHostMappedOrTracked) { context.Branch(lblEnd); @@ -385,7 +385,7 @@ namespace ARMeilleure.Instructions break; } - if (!context.Memory.Type.IsHostMappedOrTracked()) + if (!context.Memory.Type.IsHostMappedOrTracked) { context.Branch(lblEnd); @@ -399,11 +399,11 @@ namespace ARMeilleure.Instructions public static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblSlowPath, bool write, int size) { - if (context.Memory.Type.IsHostMapped()) + if (context.Memory.Type.IsHostMapped) { return EmitHostMappedPointer(context, address); } - else if (context.Memory.Type.IsHostTracked()) + else if (context.Memory.Type.IsHostTracked) { if (address.Type == OperandType.I32) { diff --git a/src/ARMeilleure/Instructions/SoftFallback.cs b/src/ARMeilleure/Instructions/SoftFallback.cs deleted file mode 100644 index c227156e5..000000000 --- a/src/ARMeilleure/Instructions/SoftFallback.cs +++ /dev/null @@ -1,692 +0,0 @@ -using ARMeilleure.State; -using System; -using System.Runtime.InteropServices; - -namespace ARMeilleure.Instructions -{ - static class SoftFallback - { - #region "ShrImm64" - [UnmanagedCallersOnly] - public static long SignedShrImm64(long value, long roundConst, int shift) - { - if (roundConst == 0L) - { - if (shift <= 63) - { - return value >> shift; - } - else /* if (shift == 64) */ - { - if (value < 0L) - { - return -1L; - } - else /* if (value >= 0L) */ - { - return 0L; - } - } - } - else /* if (roundConst == 1L << (shift - 1)) */ - { - if (shift <= 63) - { - long add = value + roundConst; - - if ((~value & (value ^ add)) < 0L) - { - return (long)((ulong)add >> shift); - } - else - { - return add >> shift; - } - } - else /* if (shift == 64) */ - { - return 0L; - } - } - } - - [UnmanagedCallersOnly] - public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift) - { - if (roundConst == 0L) - { - if (shift <= 63) - { - return value >> shift; - } - else /* if (shift == 64) */ - { - return 0UL; - } - } - else /* if (roundConst == 1L << (shift - 1)) */ - { - ulong add = value + (ulong)roundConst; - - if ((add < value) && (add < (ulong)roundConst)) - { - if (shift <= 63) - { - return (add >> shift) | (0x8000000000000000UL >> (shift - 1)); - } - else /* if (shift == 64) */ - { - return 1UL; - } - } - else - { - if (shift <= 63) - { - return add >> shift; - } - else /* if (shift == 64) */ - { - return 0UL; - } - } - } - } - #endregion - - #region "Saturation" - [UnmanagedCallersOnly] - public static int SatF32ToS32(float value) - { - if (float.IsNaN(value)) - { - return 0; - } - - return value >= int.MaxValue ? int.MaxValue : - value <= int.MinValue ? int.MinValue : (int)value; - } - - [UnmanagedCallersOnly] - public static long SatF32ToS64(float value) - { - if (float.IsNaN(value)) - { - return 0; - } - - return value >= long.MaxValue ? long.MaxValue : - value <= long.MinValue ? long.MinValue : (long)value; - } - - [UnmanagedCallersOnly] - public static uint SatF32ToU32(float value) - { - if (float.IsNaN(value)) - { - return 0; - } - - return value >= uint.MaxValue ? uint.MaxValue : - value <= uint.MinValue ? uint.MinValue : (uint)value; - } - - [UnmanagedCallersOnly] - public static ulong SatF32ToU64(float value) - { - if (float.IsNaN(value)) - { - return 0; - } - - return value >= ulong.MaxValue ? ulong.MaxValue : - value <= ulong.MinValue ? ulong.MinValue : (ulong)value; - } - - [UnmanagedCallersOnly] - public static int SatF64ToS32(double value) - { - if (double.IsNaN(value)) - { - return 0; - } - - return value >= int.MaxValue ? int.MaxValue : - value <= int.MinValue ? int.MinValue : (int)value; - } - - [UnmanagedCallersOnly] - public static long SatF64ToS64(double value) - { - if (double.IsNaN(value)) - { - return 0; - } - - return value >= long.MaxValue ? long.MaxValue : - value <= long.MinValue ? long.MinValue : (long)value; - } - - [UnmanagedCallersOnly] - public static uint SatF64ToU32(double value) - { - if (double.IsNaN(value)) - { - return 0; - } - - return value >= uint.MaxValue ? uint.MaxValue : - value <= uint.MinValue ? uint.MinValue : (uint)value; - } - - [UnmanagedCallersOnly] - public static ulong SatF64ToU64(double value) - { - if (double.IsNaN(value)) - { - return 0; - } - - return value >= ulong.MaxValue ? ulong.MaxValue : - value <= ulong.MinValue ? ulong.MinValue : (ulong)value; - } - #endregion - - #region "Count" - [UnmanagedCallersOnly] - public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). - { - value ^= value >> 1; - - int highBit = size - 2; - - for (int bit = highBit; bit >= 0; bit--) - { - if (((int)(value >> bit) & 0b1) != 0) - { - return (ulong)(highBit - bit); - } - } - - return (ulong)(size - 1); - } - - private static ReadOnlySpan ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]; - - [UnmanagedCallersOnly] - public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). - { - if (value == 0ul) - { - return (ulong)size; - } - - int nibbleIdx = size; - int preCount, count = 0; - - do - { - nibbleIdx -= 4; - preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111]; - count += preCount; - } - while (preCount == 4); - - return (ulong)count; - } - #endregion - - #region "Table" - [UnmanagedCallersOnly] - public static V128 Tbl1(V128 vector, int bytes, V128 tb0) - { - return TblOrTbx(default, vector, bytes, tb0); - } - - [UnmanagedCallersOnly] - public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1) - { - return TblOrTbx(default, vector, bytes, tb0, tb1); - } - - [UnmanagedCallersOnly] - public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2) - { - return TblOrTbx(default, vector, bytes, tb0, tb1, tb2); - } - - [UnmanagedCallersOnly] - public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3) - { - return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3); - } - - [UnmanagedCallersOnly] - public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0) - { - return TblOrTbx(dest, vector, bytes, tb0); - } - - [UnmanagedCallersOnly] - public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1) - { - return TblOrTbx(dest, vector, bytes, tb0, tb1); - } - - [UnmanagedCallersOnly] - public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2) - { - return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2); - } - - [UnmanagedCallersOnly] - public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3) - { - return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3); - } - - private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params ReadOnlySpan tb) - { - byte[] res = new byte[16]; - - if (dest != default) - { - Buffer.BlockCopy(dest.ToArray(), 0, res, 0, bytes); - } - - byte[] table = new byte[tb.Length * 16]; - - for (byte index = 0; index < tb.Length; index++) - { - Buffer.BlockCopy(tb[index].ToArray(), 0, table, index * 16, 16); - } - - byte[] v = vector.ToArray(); - - for (byte index = 0; index < bytes; index++) - { - byte tblIndex = v[index]; - - if (tblIndex < table.Length) - { - res[index] = table[tblIndex]; - } - } - - return new V128(res); - } - #endregion - - #region "Crc32" - private const uint Crc32RevPoly = 0xedb88320; - private const uint Crc32cRevPoly = 0x82f63b78; - - [UnmanagedCallersOnly] - public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value); - [UnmanagedCallersOnly] - public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value); - [UnmanagedCallersOnly] - public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value); - [UnmanagedCallersOnly] - public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value); - - [UnmanagedCallersOnly] - public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value); - [UnmanagedCallersOnly] - public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value); - [UnmanagedCallersOnly] - public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value); - [UnmanagedCallersOnly] - public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value); - - private static uint Crc32h(uint crc, uint poly, ushort val) - { - crc = Crc32(crc, poly, (byte)(val >> 0)); - crc = Crc32(crc, poly, (byte)(val >> 8)); - - return crc; - } - - private static uint Crc32w(uint crc, uint poly, uint val) - { - crc = Crc32(crc, poly, (byte)(val >> 0)); - crc = Crc32(crc, poly, (byte)(val >> 8)); - crc = Crc32(crc, poly, (byte)(val >> 16)); - crc = Crc32(crc, poly, (byte)(val >> 24)); - - return crc; - } - - private static uint Crc32x(uint crc, uint poly, ulong val) - { - crc = Crc32(crc, poly, (byte)(val >> 0)); - crc = Crc32(crc, poly, (byte)(val >> 8)); - crc = Crc32(crc, poly, (byte)(val >> 16)); - crc = Crc32(crc, poly, (byte)(val >> 24)); - crc = Crc32(crc, poly, (byte)(val >> 32)); - crc = Crc32(crc, poly, (byte)(val >> 40)); - crc = Crc32(crc, poly, (byte)(val >> 48)); - crc = Crc32(crc, poly, (byte)(val >> 56)); - - return crc; - } - - private static uint Crc32(uint crc, uint poly, byte val) - { - crc ^= val; - - for (int bit = 7; bit >= 0; bit--) - { - uint mask = (uint)(-(int)(crc & 1)); - - crc = (crc >> 1) ^ (poly & mask); - } - - return crc; - } - #endregion - - #region "Aes" - [UnmanagedCallersOnly] - public static V128 Decrypt(V128 value, V128 roundKey) - { - return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey)); - } - - [UnmanagedCallersOnly] - public static V128 Encrypt(V128 value, V128 roundKey) - { - return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey)); - } - - [UnmanagedCallersOnly] - public static V128 InverseMixColumns(V128 value) - { - return CryptoHelper.AesInvMixColumns(value); - } - - [UnmanagedCallersOnly] - public static V128 MixColumns(V128 value) - { - return CryptoHelper.AesMixColumns(value); - } - #endregion - - #region "Sha1" - [UnmanagedCallersOnly] - public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk) - { - for (int e = 0; e <= 3; e++) - { - uint t = ShaChoose(hash_abcd.Extract(1), - hash_abcd.Extract(2), - hash_abcd.Extract(3)); - - hash_e += Rol(hash_abcd.Extract(0), 5) + t + wk.Extract(e); - - t = Rol(hash_abcd.Extract(1), 30); - - hash_abcd.Insert(1, t); - - Rol32_160(ref hash_e, ref hash_abcd); - } - - return hash_abcd; - } - - [UnmanagedCallersOnly] - public static uint FixedRotate(uint hash_e) - { - return hash_e.Rol(30); - } - - [UnmanagedCallersOnly] - public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk) - { - for (int e = 0; e <= 3; e++) - { - uint t = ShaMajority(hash_abcd.Extract(1), - hash_abcd.Extract(2), - hash_abcd.Extract(3)); - - hash_e += Rol(hash_abcd.Extract(0), 5) + t + wk.Extract(e); - - t = Rol(hash_abcd.Extract(1), 30); - - hash_abcd.Insert(1, t); - - Rol32_160(ref hash_e, ref hash_abcd); - } - - return hash_abcd; - } - - [UnmanagedCallersOnly] - public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk) - { - for (int e = 0; e <= 3; e++) - { - uint t = ShaParity(hash_abcd.Extract(1), - hash_abcd.Extract(2), - hash_abcd.Extract(3)); - - hash_e += Rol(hash_abcd.Extract(0), 5) + t + wk.Extract(e); - - t = Rol(hash_abcd.Extract(1), 30); - - hash_abcd.Insert(1, t); - - Rol32_160(ref hash_e, ref hash_abcd); - } - - return hash_abcd; - } - - [UnmanagedCallersOnly] - public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11) - { - ulong t2 = w4_7.Extract(0); - ulong t1 = w0_3.Extract(1); - - V128 result = new(t1, t2); - - return result ^ (w0_3 ^ w8_11); - } - - [UnmanagedCallersOnly] - public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15) - { - V128 t = tw0_3 ^ (w12_15 >> 32); - - uint tE0 = t.Extract(0); - uint tE1 = t.Extract(1); - uint tE2 = t.Extract(2); - uint tE3 = t.Extract(3); - - return new V128(tE0.Rol(1), tE1.Rol(1), tE2.Rol(1), tE3.Rol(1) ^ tE0.Rol(2)); - } - - private static void Rol32_160(ref uint y, ref V128 x) - { - uint xE3 = x.Extract(3); - - x <<= 32; - x.Insert(0, y); - - y = xE3; - } - - private static uint ShaChoose(uint x, uint y, uint z) - { - return ((y ^ z) & x) ^ z; - } - - private static uint ShaMajority(uint x, uint y, uint z) - { - return (x & y) | ((x | y) & z); - } - - private static uint ShaParity(uint x, uint y, uint z) - { - return x ^ y ^ z; - } - - private static uint Rol(this uint value, int count) - { - return (value << count) | (value >> (32 - count)); - } - #endregion - - #region "Sha256" - [UnmanagedCallersOnly] - public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk) - { - return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true); - } - - [UnmanagedCallersOnly] - public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk) - { - return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false); - } - - [UnmanagedCallersOnly] - public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7) - { - V128 result = new(); - - for (int e = 0; e <= 3; e++) - { - uint elt = (e <= 2 ? w0_3 : w4_7).Extract(e <= 2 ? e + 1 : 0); - - elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3); - - elt += w0_3.Extract(e); - - result.Insert(e, elt); - } - - return result; - } - - [UnmanagedCallersOnly] - public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15) - { - V128 result = new(); - - ulong t1 = w12_15.Extract(1); - - for (int e = 0; e <= 1; e++) - { - uint elt = t1.ULongPart(e); - - elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); - - elt += w0_3.Extract(e) + w8_11.Extract(e + 1); - - result.Insert(e, elt); - } - - t1 = result.Extract(0); - - for (int e = 2; e <= 3; e++) - { - uint elt = t1.ULongPart(e - 2); - - elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); - - elt += w0_3.Extract(e) + (e == 2 ? w8_11 : w12_15).Extract(e == 2 ? 3 : 0); - - result.Insert(e, elt); - } - - return result; - } - - private static V128 Sha256Hash(V128 x, V128 y, V128 w, bool part1) - { - for (int e = 0; e <= 3; e++) - { - uint chs = ShaChoose(y.Extract(0), - y.Extract(1), - y.Extract(2)); - - uint maj = ShaMajority(x.Extract(0), - x.Extract(1), - x.Extract(2)); - - uint t1 = y.Extract(3) + ShaHashSigma1(y.Extract(0)) + chs + w.Extract(e); - - uint t2 = t1 + x.Extract(3); - - x.Insert(3, t2); - - t2 = t1 + ShaHashSigma0(x.Extract(0)) + maj; - - y.Insert(3, t2); - - Rol32_256(ref y, ref x); - } - - return part1 ? x : y; - } - - private static void Rol32_256(ref V128 y, ref V128 x) - { - uint yE3 = y.Extract(3); - uint xE3 = x.Extract(3); - - y <<= 32; - x <<= 32; - - y.Insert(0, xE3); - x.Insert(0, yE3); - } - - private static uint ShaHashSigma0(uint x) - { - return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22); - } - - private static uint ShaHashSigma1(uint x) - { - return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25); - } - - private static uint Ror(this uint value, int count) - { - return (value >> count) | (value << (32 - count)); - } - - private static uint Lsr(this uint value, int count) - { - return value >> count; - } - - private static uint ULongPart(this ulong value, int part) - { - return part == 0 - ? (uint)(value & 0xFFFFFFFFUL) - : (uint)(value >> 32); - } - #endregion - - [UnmanagedCallersOnly] - public static V128 PolynomialMult64_128(ulong op1, ulong op2) - { - V128 result = V128.Zero; - - V128 op2_128 = new(op2, 0); - - for (int i = 0; i < 64; i++) - { - if (((op1 >> i) & 1) == 1) - { - result ^= op2_128 << i; - } - } - - return result; - } - } -} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Aes.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Aes.cs new file mode 100644 index 000000000..fe133929d --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Aes.cs @@ -0,0 +1,32 @@ +using ARMeilleure.State; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + [UnmanagedCallersOnly] + public static V128 Decrypt(V128 value, V128 roundKey) + { + return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey)); + } + + [UnmanagedCallersOnly] + public static V128 Encrypt(V128 value, V128 roundKey) + { + return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey)); + } + + [UnmanagedCallersOnly] + public static V128 InverseMixColumns(V128 value) + { + return CryptoHelper.AesInvMixColumns(value); + } + + [UnmanagedCallersOnly] + public static V128 MixColumns(V128 value) + { + return CryptoHelper.AesMixColumns(value); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Count.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Count.cs new file mode 100644 index 000000000..cbe2a79ca --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Count.cs @@ -0,0 +1,50 @@ +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + [UnmanagedCallersOnly] + public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). + { + value ^= value >> 1; + + int highBit = size - 2; + + for (int bit = highBit; bit >= 0; bit--) + { + if (((int)(value >> bit) & 0b1) != 0) + { + return (ulong)(highBit - bit); + } + } + + return (ulong)(size - 1); + } + + private static ReadOnlySpan ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]; + + [UnmanagedCallersOnly] + public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). + { + if (value == 0ul) + { + return (ulong)size; + } + + int nibbleIdx = size; + int preCount, count = 0; + + do + { + nibbleIdx -= 4; + preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111]; + count += preCount; + } + while (preCount == 4); + + return (ulong)count; + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Crc32.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Crc32.cs new file mode 100644 index 000000000..505cb032f --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Crc32.cs @@ -0,0 +1,74 @@ +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + private const uint Crc32RevPoly = 0xedb88320; + private const uint Crc32cRevPoly = 0x82f63b78; + + [UnmanagedCallersOnly] + public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value); + [UnmanagedCallersOnly] + public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value); + [UnmanagedCallersOnly] + public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value); + [UnmanagedCallersOnly] + public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value); + + [UnmanagedCallersOnly] + public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value); + [UnmanagedCallersOnly] + public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value); + [UnmanagedCallersOnly] + public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value); + [UnmanagedCallersOnly] + public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value); + + private static uint Crc32h(uint crc, uint poly, ushort val) + { + crc = Crc32(crc, poly, (byte)(val >> 0)); + crc = Crc32(crc, poly, (byte)(val >> 8)); + + return crc; + } + + private static uint Crc32w(uint crc, uint poly, uint val) + { + crc = Crc32(crc, poly, (byte)(val >> 0)); + crc = Crc32(crc, poly, (byte)(val >> 8)); + crc = Crc32(crc, poly, (byte)(val >> 16)); + crc = Crc32(crc, poly, (byte)(val >> 24)); + + return crc; + } + + private static uint Crc32x(uint crc, uint poly, ulong val) + { + crc = Crc32(crc, poly, (byte)(val >> 0)); + crc = Crc32(crc, poly, (byte)(val >> 8)); + crc = Crc32(crc, poly, (byte)(val >> 16)); + crc = Crc32(crc, poly, (byte)(val >> 24)); + crc = Crc32(crc, poly, (byte)(val >> 32)); + crc = Crc32(crc, poly, (byte)(val >> 40)); + crc = Crc32(crc, poly, (byte)(val >> 48)); + crc = Crc32(crc, poly, (byte)(val >> 56)); + + return crc; + } + + private static uint Crc32(uint crc, uint poly, byte val) + { + crc ^= val; + + for (int bit = 7; bit >= 0; bit--) + { + uint mask = (uint)(-(int)(crc & 1)); + + crc = (crc >> 1) ^ (poly & mask); + } + + return crc; + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Saturation.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Saturation.cs new file mode 100644 index 000000000..2cbea1560 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Saturation.cs @@ -0,0 +1,103 @@ +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + [UnmanagedCallersOnly] + public static int SatF32ToS32(float value) + { + if (float.IsNaN(value)) + { + return 0; + } + + return value >= int.MaxValue ? int.MaxValue : + value <= int.MinValue ? int.MinValue : (int)value; + } + + [UnmanagedCallersOnly] + public static long SatF32ToS64(float value) + { + if (float.IsNaN(value)) + { + return 0; + } + + return value >= long.MaxValue ? long.MaxValue : + value <= long.MinValue ? long.MinValue : (long)value; + } + + [UnmanagedCallersOnly] + public static uint SatF32ToU32(float value) + { + if (float.IsNaN(value)) + { + return 0; + } + + return value >= uint.MaxValue ? uint.MaxValue : + value <= uint.MinValue ? uint.MinValue : (uint)value; + } + + [UnmanagedCallersOnly] + public static ulong SatF32ToU64(float value) + { + if (float.IsNaN(value)) + { + return 0; + } + + return value >= ulong.MaxValue ? ulong.MaxValue : + value <= ulong.MinValue ? ulong.MinValue : (ulong)value; + } + + [UnmanagedCallersOnly] + public static int SatF64ToS32(double value) + { + if (double.IsNaN(value)) + { + return 0; + } + + return value >= int.MaxValue ? int.MaxValue : + value <= int.MinValue ? int.MinValue : (int)value; + } + + [UnmanagedCallersOnly] + public static long SatF64ToS64(double value) + { + if (double.IsNaN(value)) + { + return 0; + } + + return value >= long.MaxValue ? long.MaxValue : + value <= long.MinValue ? long.MinValue : (long)value; + } + + [UnmanagedCallersOnly] + public static uint SatF64ToU32(double value) + { + if (double.IsNaN(value)) + { + return 0; + } + + return value >= uint.MaxValue ? uint.MaxValue : + value <= uint.MinValue ? uint.MinValue : (uint)value; + } + + [UnmanagedCallersOnly] + public static ulong SatF64ToU64(double value) + { + if (double.IsNaN(value)) + { + return 0; + } + + return value >= ulong.MaxValue ? ulong.MaxValue : + value <= ulong.MinValue ? ulong.MinValue : (ulong)value; + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Sha1.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Sha1.cs new file mode 100644 index 000000000..62aecced7 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Sha1.cs @@ -0,0 +1,131 @@ +using ARMeilleure.State; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + [UnmanagedCallersOnly] + public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaChoose(hash_abcd.Extract(1), + hash_abcd.Extract(2), + hash_abcd.Extract(3)); + + hash_e += Rol(hash_abcd.Extract(0), 5) + t + wk.Extract(e); + + t = Rol(hash_abcd.Extract(1), 30); + + hash_abcd.Insert(1, t); + + Rol32_160(ref hash_e, ref hash_abcd); + } + + return hash_abcd; + } + + [UnmanagedCallersOnly] + public static uint FixedRotate(uint hash_e) + { + return hash_e.Rol(30); + } + + [UnmanagedCallersOnly] + public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaMajority(hash_abcd.Extract(1), + hash_abcd.Extract(2), + hash_abcd.Extract(3)); + + hash_e += Rol(hash_abcd.Extract(0), 5) + t + wk.Extract(e); + + t = Rol(hash_abcd.Extract(1), 30); + + hash_abcd.Insert(1, t); + + Rol32_160(ref hash_e, ref hash_abcd); + } + + return hash_abcd; + } + + [UnmanagedCallersOnly] + public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaParity(hash_abcd.Extract(1), + hash_abcd.Extract(2), + hash_abcd.Extract(3)); + + hash_e += Rol(hash_abcd.Extract(0), 5) + t + wk.Extract(e); + + t = Rol(hash_abcd.Extract(1), 30); + + hash_abcd.Insert(1, t); + + Rol32_160(ref hash_e, ref hash_abcd); + } + + return hash_abcd; + } + + [UnmanagedCallersOnly] + public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11) + { + ulong t2 = w4_7.Extract(0); + ulong t1 = w0_3.Extract(1); + + V128 result = new(t1, t2); + + return result ^ (w0_3 ^ w8_11); + } + + [UnmanagedCallersOnly] + public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15) + { + V128 t = tw0_3 ^ (w12_15 >> 32); + + uint tE0 = t.Extract(0); + uint tE1 = t.Extract(1); + uint tE2 = t.Extract(2); + uint tE3 = t.Extract(3); + + return new V128(tE0.Rol(1), tE1.Rol(1), tE2.Rol(1), tE3.Rol(1) ^ tE0.Rol(2)); + } + + private static void Rol32_160(ref uint y, ref V128 x) + { + uint xE3 = x.Extract(3); + + x <<= 32; + x.Insert(0, y); + + y = xE3; + } + + private static uint ShaChoose(uint x, uint y, uint z) + { + return ((y ^ z) & x) ^ z; + } + + private static uint ShaMajority(uint x, uint y, uint z) + { + return (x & y) | ((x | y) & z); + } + + private static uint ShaParity(uint x, uint y, uint z) + { + return x ^ y ^ z; + } + + private static uint Rol(this uint value, int count) + { + return (value << count) | (value >> (32 - count)); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Sha256.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Sha256.cs new file mode 100644 index 000000000..9ce5ec5f9 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Sha256.cs @@ -0,0 +1,140 @@ +using ARMeilleure.State; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + [UnmanagedCallersOnly] + public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk) + { + return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true); + } + + [UnmanagedCallersOnly] + public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk) + { + return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false); + } + + [UnmanagedCallersOnly] + public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7) + { + V128 result = new(); + + for (int e = 0; e <= 3; e++) + { + uint elt = (e <= 2 ? w0_3 : w4_7).Extract(e <= 2 ? e + 1 : 0); + + elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3); + + elt += w0_3.Extract(e); + + result.Insert(e, elt); + } + + return result; + } + + [UnmanagedCallersOnly] + public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15) + { + V128 result = new(); + + ulong t1 = w12_15.Extract(1); + + for (int e = 0; e <= 1; e++) + { + uint elt = t1.ULongPart(e); + + elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); + + elt += w0_3.Extract(e) + w8_11.Extract(e + 1); + + result.Insert(e, elt); + } + + t1 = result.Extract(0); + + for (int e = 2; e <= 3; e++) + { + uint elt = t1.ULongPart(e - 2); + + elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); + + elt += w0_3.Extract(e) + (e == 2 ? w8_11 : w12_15).Extract(e == 2 ? 3 : 0); + + result.Insert(e, elt); + } + + return result; + } + + private static V128 Sha256Hash(V128 x, V128 y, V128 w, bool part1) + { + for (int e = 0; e <= 3; e++) + { + uint chs = ShaChoose(y.Extract(0), + y.Extract(1), + y.Extract(2)); + + uint maj = ShaMajority(x.Extract(0), + x.Extract(1), + x.Extract(2)); + + uint t1 = y.Extract(3) + ShaHashSigma1(y.Extract(0)) + chs + w.Extract(e); + + uint t2 = t1 + x.Extract(3); + + x.Insert(3, t2); + + t2 = t1 + ShaHashSigma0(x.Extract(0)) + maj; + + y.Insert(3, t2); + + Rol32_256(ref y, ref x); + } + + return part1 ? x : y; + } + + private static void Rol32_256(ref V128 y, ref V128 x) + { + uint yE3 = y.Extract(3); + uint xE3 = x.Extract(3); + + y <<= 32; + x <<= 32; + + y.Insert(0, xE3); + x.Insert(0, yE3); + } + + private static uint ShaHashSigma0(uint x) + { + return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22); + } + + private static uint ShaHashSigma1(uint x) + { + return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25); + } + + private static uint Ror(this uint value, int count) + { + return (value >> count) | (value << (32 - count)); + } + + private static uint Lsr(this uint value, int count) + { + return value >> count; + } + + private static uint ULongPart(this ulong value, int part) + { + return part == 0 + ? (uint)(value & 0xFFFFFFFFUL) + : (uint)(value >> 32); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.ShrImm64.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.ShrImm64.cs new file mode 100644 index 000000000..063b2c939 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.ShrImm64.cs @@ -0,0 +1,93 @@ +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + [UnmanagedCallersOnly] + public static long SignedShrImm64(long value, long roundConst, int shift) + { + if (roundConst == 0L) + { + if (shift <= 63) + { + return value >> shift; + } + else /* if (shift == 64) */ + { + if (value < 0L) + { + return -1L; + } + else /* if (value >= 0L) */ + { + return 0L; + } + } + } + else /* if (roundConst == 1L << (shift - 1)) */ + { + if (shift <= 63) + { + long add = value + roundConst; + + if ((~value & (value ^ add)) < 0L) + { + return (long)((ulong)add >> shift); + } + else + { + return add >> shift; + } + } + else /* if (shift == 64) */ + { + return 0L; + } + } + } + + [UnmanagedCallersOnly] + public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift) + { + if (roundConst == 0L) + { + if (shift <= 63) + { + return value >> shift; + } + else /* if (shift == 64) */ + { + return 0UL; + } + } + else /* if (roundConst == 1L << (shift - 1)) */ + { + ulong add = value + (ulong)roundConst; + + if ((add < value) && (add < (ulong)roundConst)) + { + if (shift <= 63) + { + return (add >> shift) | (0x8000000000000000UL >> (shift - 1)); + } + else /* if (shift == 64) */ + { + return 1UL; + } + } + else + { + if (shift <= 63) + { + return add >> shift; + } + else /* if (shift == 64) */ + { + return 0UL; + } + } + } + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Table.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Table.cs new file mode 100644 index 000000000..61fa178df --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.Table.cs @@ -0,0 +1,88 @@ +using ARMeilleure.State; +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + [UnmanagedCallersOnly] + public static V128 Tbl1(V128 vector, int bytes, V128 tb0) + { + return TblOrTbx(default, vector, bytes, tb0); + } + + [UnmanagedCallersOnly] + public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1) + { + return TblOrTbx(default, vector, bytes, tb0, tb1); + } + + [UnmanagedCallersOnly] + public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2) + { + return TblOrTbx(default, vector, bytes, tb0, tb1, tb2); + } + + [UnmanagedCallersOnly] + public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3) + { + return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3); + } + + [UnmanagedCallersOnly] + public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0) + { + return TblOrTbx(dest, vector, bytes, tb0); + } + + [UnmanagedCallersOnly] + public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1) + { + return TblOrTbx(dest, vector, bytes, tb0, tb1); + } + + [UnmanagedCallersOnly] + public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2) + { + return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2); + } + + [UnmanagedCallersOnly] + public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3) + { + return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3); + } + + private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params ReadOnlySpan tb) + { + byte[] res = new byte[16]; + + if (dest != default) + { + Buffer.BlockCopy(dest.ToArray(), 0, res, 0, bytes); + } + + byte[] table = new byte[tb.Length * 16]; + + for (byte index = 0; index < tb.Length; index++) + { + Buffer.BlockCopy(tb[index].ToArray(), 0, table, index * 16, 16); + } + + byte[] v = vector.ToArray(); + + for (byte index = 0; index < bytes; index++) + { + byte tblIndex = v[index]; + + if (tblIndex < table.Length) + { + res[index] = table[tblIndex]; + } + } + + return new V128(res); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.cs b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.cs new file mode 100644 index 000000000..a5baae782 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFallback/SoftFallback.cs @@ -0,0 +1,26 @@ +using ARMeilleure.State; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static partial class SoftFallback + { + [UnmanagedCallersOnly] + public static V128 PolynomialMult64_128(ulong op1, ulong op2) + { + V128 result = V128.Zero; + + V128 op2_128 = new(op2, 0); + + for (int i = 0; i < 64; i++) + { + if (((op1 >> i) & 1) == 1) + { + result ^= op2_128 << i; + } + } + + return result; + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFloat.cs b/src/ARMeilleure/Instructions/SoftFloat.cs deleted file mode 100644 index ccc45cc64..000000000 --- a/src/ARMeilleure/Instructions/SoftFloat.cs +++ /dev/null @@ -1,3735 +0,0 @@ -using ARMeilleure.State; -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace ARMeilleure.Instructions -{ - static class SoftFloat - { - static SoftFloat() - { - RecipEstimateTable = BuildRecipEstimateTable(); - RecipSqrtEstimateTable = BuildRecipSqrtEstimateTable(); - } - - public static readonly byte[] RecipEstimateTable; - public static readonly byte[] RecipSqrtEstimateTable; - - private static byte[] BuildRecipEstimateTable() - { - byte[] tbl = new byte[256]; - - for (int idx = 0; idx < 256; idx++) - { - uint src = (uint)idx + 256u; - - Debug.Assert(src is >= 256u and < 512u); - - src = (src << 1) + 1u; - - uint aux = (1u << 19) / src; - - uint dst = (aux + 1u) >> 1; - - Debug.Assert(dst is >= 256u and < 512u); - - tbl[idx] = (byte)(dst - 256u); - } - - return tbl; - } - - private static byte[] BuildRecipSqrtEstimateTable() - { - byte[] tbl = new byte[384]; - - for (int idx = 0; idx < 384; idx++) - { - uint src = (uint)idx + 128u; - - Debug.Assert(src is >= 128u and < 512u); - - if (src < 256u) - { - src = (src << 1) + 1u; - } - else - { - src = (src >> 1) << 1; - src = (src + 1u) << 1; - } - - uint aux = 512u; - - while (src * (aux + 1u) * (aux + 1u) < (1u << 28)) - { - aux++; - } - - uint dst = (aux + 1u) >> 1; - - Debug.Assert(dst is >= 256u and < 512u); - - tbl[idx] = (byte)(dst - 256u); - } - - return tbl; - } - - public static void FPProcessException(FPException exc, ExecutionContext context) - { - FPProcessException(exc, context, context.Fpcr); - } - - public static void FPProcessException(FPException exc, ExecutionContext context, FPCR fpcr) - { - int enable = (int)exc + 8; - - if ((fpcr & (FPCR)(1 << enable)) != 0) - { - throw new NotImplementedException("Floating-point trap handling."); - } - else - { - context.Fpsr |= (FPSR)(1 << (int)exc); - } - } - - public static FPRoundingMode GetRoundingMode(this FPCR fpcr) - { - const int RModeShift = 22; - - return (FPRoundingMode)(((uint)fpcr >> RModeShift) & 3u); - } - } - - static class SoftFloat16 - { - public static ushort FPDefaultNaN() - { - return (ushort)0x7E00u; - } - - public static ushort FPInfinity(bool sign) - { - return sign ? (ushort)0xFC00u : (ushort)0x7C00u; - } - - public static ushort FPZero(bool sign) - { - return sign ? (ushort)0x8000u : (ushort)0x0000u; - } - - public static ushort FPMaxNormal(bool sign) - { - return sign ? (ushort)0xFBFFu : (ushort)0x7BFFu; - } - - public static double FPUnpackCv( - this ushort valueBits, - out FPType type, - out bool sign, - ExecutionContext context) - { - sign = (~(uint)valueBits & 0x8000u) == 0u; - - uint exp16 = ((uint)valueBits & 0x7C00u) >> 10; - uint frac16 = (uint)valueBits & 0x03FFu; - - double real; - - if (exp16 == 0u) - { - if (frac16 == 0u) - { - type = FPType.Zero; - real = 0d; - } - else - { - type = FPType.Nonzero; // Subnormal. - real = Math.Pow(2d, -14) * ((double)frac16 * Math.Pow(2d, -10)); - } - } - else if (exp16 == 0x1Fu && (context.Fpcr & FPCR.Ahp) == 0) - { - if (frac16 == 0u) - { - type = FPType.Infinity; - real = Math.Pow(2d, 1000); - } - else - { - type = (~frac16 & 0x0200u) == 0u ? FPType.QNaN : FPType.SNaN; - real = 0d; - } - } - else - { - type = FPType.Nonzero; // Normal. - real = Math.Pow(2d, (int)exp16 - 15) * (1d + (double)frac16 * Math.Pow(2d, -10)); - } - - return sign ? -real : real; - } - - public static ushort FPRoundCv(double real, ExecutionContext context) - { - const int MinimumExp = -14; - - const int E = 5; - const int F = 10; - - bool sign; - double mantissa; - - if (real < 0d) - { - sign = true; - mantissa = -real; - } - else - { - sign = false; - mantissa = real; - } - - int exponent = 0; - - while (mantissa < 1d) - { - mantissa *= 2d; - exponent--; - } - - while (mantissa >= 2d) - { - mantissa /= 2d; - exponent++; - } - - uint biasedExp = (uint)Math.Max(exponent - MinimumExp + 1, 0); - - if (biasedExp == 0u) - { - 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; - - if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) - { - SoftFloat.FPProcessException(FPException.Underflow, context); - } - - bool overflowToInf; - bool roundUp; - - switch (context.Fpcr.GetRoundingMode()) - { - case FPRoundingMode.ToNearest: - roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); - overflowToInf = true; - break; - - case FPRoundingMode.TowardsPlusInfinity: - roundUp = (error != 0d && !sign); - overflowToInf = !sign; - break; - - case FPRoundingMode.TowardsMinusInfinity: - roundUp = (error != 0d && sign); - overflowToInf = sign; - break; - - case FPRoundingMode.TowardsZero: - roundUp = false; - overflowToInf = false; - break; - - default: - throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.GetRoundingMode()}\"."); - } - - if (roundUp) - { - intMant++; - - if (intMant == 1u << F) - { - biasedExp = 1u; - } - - if (intMant == 1u << (F + 1)) - { - biasedExp++; - intMant >>= 1; - } - } - - ushort resultBits; - - if ((context.Fpcr & FPCR.Ahp) == 0) - { - if (biasedExp >= (1u << E) - 1u) - { - resultBits = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); - - SoftFloat.FPProcessException(FPException.Overflow, context); - - error = 1d; - } - else - { - resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu)); - } - } - else - { - if (biasedExp >= 1u << E) - { - resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); - - SoftFloat.FPProcessException(FPException.InvalidOp, context); - - error = 0d; - } - else - { - resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu)); - } - } - - if (error != 0d) - { - SoftFloat.FPProcessException(FPException.Inexact, context); - } - - return resultBits; - } - } - - static class SoftFloat16_32 - { - [UnmanagedCallersOnly] - public static float FPConvert(ushort valueBits) - { - ExecutionContext context = NativeInterface.GetContext(); - - double real = valueBits.FPUnpackCv(out FPType type, out bool sign, context); - - float result; - - if (type is FPType.SNaN or FPType.QNaN) - { - if ((context.Fpcr & FPCR.Dn) != 0) - { - result = SoftFloat32.FPDefaultNaN(); - } - else - { - result = FPConvertNaN(valueBits); - } - - if (type == FPType.SNaN) - { - SoftFloat.FPProcessException(FPException.InvalidOp, context); - } - } - else if (type == FPType.Infinity) - { - result = SoftFloat32.FPInfinity(sign); - } - else if (type == FPType.Zero) - { - result = SoftFloat32.FPZero(sign); - } - else - { - result = FPRoundCv(real, context); - } - - return result; - } - - private static float FPRoundCv(double real, ExecutionContext context) - { - const int MinimumExp = -126; - - const int E = 8; - const int F = 23; - - bool sign; - double mantissa; - - if (real < 0d) - { - sign = true; - mantissa = -real; - } - else - { - sign = false; - mantissa = real; - } - - int exponent = 0; - - while (mantissa < 1d) - { - mantissa *= 2d; - exponent--; - } - - while (mantissa >= 2d) - { - mantissa /= 2d; - exponent++; - } - - 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); - - if (biasedExp == 0u) - { - 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; - - if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) - { - SoftFloat.FPProcessException(FPException.Underflow, context); - } - - bool overflowToInf; - bool roundUp; - - switch (context.Fpcr.GetRoundingMode()) - { - case FPRoundingMode.ToNearest: - roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); - overflowToInf = true; - break; - - case FPRoundingMode.TowardsPlusInfinity: - roundUp = (error != 0d && !sign); - overflowToInf = !sign; - break; - - case FPRoundingMode.TowardsMinusInfinity: - roundUp = (error != 0d && sign); - overflowToInf = sign; - break; - - case FPRoundingMode.TowardsZero: - roundUp = false; - overflowToInf = false; - break; - - default: - throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.GetRoundingMode()}\"."); - } - - if (roundUp) - { - intMant++; - - if (intMant == 1u << F) - { - biasedExp = 1u; - } - - if (intMant == 1u << (F + 1)) - { - biasedExp++; - intMant >>= 1; - } - } - - float result; - - if (biasedExp >= (1u << E) - 1u) - { - result = overflowToInf ? SoftFloat32.FPInfinity(sign) : SoftFloat32.FPMaxNormal(sign); - - SoftFloat.FPProcessException(FPException.Overflow, context); - - error = 1d; - } - else - { - result = BitConverter.Int32BitsToSingle( - (int)((sign ? 1u : 0u) << 31 | (biasedExp & 0xFFu) << 23 | (intMant & 0x007FFFFFu))); - } - - if (error != 0d) - { - SoftFloat.FPProcessException(FPException.Inexact, context); - } - - return result; - } - - private static float FPConvertNaN(ushort valueBits) - { - return BitConverter.Int32BitsToSingle( - (int)(((uint)valueBits & 0x8000u) << 16 | 0x7FC00000u | ((uint)valueBits & 0x01FFu) << 13)); - } - } - - static class SoftFloat16_64 - { - [UnmanagedCallersOnly] - public static double FPConvert(ushort valueBits) - { - ExecutionContext context = NativeInterface.GetContext(); - - double real = valueBits.FPUnpackCv(out FPType type, out bool sign, context); - - double result; - - if (type is FPType.SNaN or FPType.QNaN) - { - if ((context.Fpcr & FPCR.Dn) != 0) - { - result = SoftFloat64.FPDefaultNaN(); - } - else - { - result = FPConvertNaN(valueBits); - } - - if (type == FPType.SNaN) - { - SoftFloat.FPProcessException(FPException.InvalidOp, context); - } - } - else if (type == FPType.Infinity) - { - result = SoftFloat64.FPInfinity(sign); - } - else if (type == FPType.Zero) - { - result = SoftFloat64.FPZero(sign); - } - else - { - result = FPRoundCv(real, context); - } - - return result; - } - - private static double FPRoundCv(double real, ExecutionContext context) - { - const int MinimumExp = -1022; - - const int E = 11; - const int F = 52; - - bool sign; - double mantissa; - - if (real < 0d) - { - sign = true; - mantissa = -real; - } - else - { - sign = false; - mantissa = real; - } - - int exponent = 0; - - while (mantissa < 1d) - { - mantissa *= 2d; - exponent--; - } - - while (mantissa >= 2d) - { - mantissa /= 2d; - exponent++; - } - - 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); - - if (biasedExp == 0u) - { - 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; - - if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) - { - SoftFloat.FPProcessException(FPException.Underflow, context); - } - - bool overflowToInf; - bool roundUp; - - switch (context.Fpcr.GetRoundingMode()) - { - case FPRoundingMode.ToNearest: - roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); - overflowToInf = true; - break; - - case FPRoundingMode.TowardsPlusInfinity: - roundUp = (error != 0d && !sign); - overflowToInf = !sign; - break; - - case FPRoundingMode.TowardsMinusInfinity: - roundUp = (error != 0d && sign); - overflowToInf = sign; - break; - - case FPRoundingMode.TowardsZero: - roundUp = false; - overflowToInf = false; - break; - - default: - throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.GetRoundingMode()}\"."); - } - - if (roundUp) - { - intMant++; - - if (intMant == 1ul << F) - { - biasedExp = 1u; - } - - if (intMant == 1ul << (F + 1)) - { - biasedExp++; - intMant >>= 1; - } - } - - double result; - - if (biasedExp >= (1u << E) - 1u) - { - result = overflowToInf ? SoftFloat64.FPInfinity(sign) : SoftFloat64.FPMaxNormal(sign); - - SoftFloat.FPProcessException(FPException.Overflow, context); - - error = 1d; - } - else - { - result = BitConverter.Int64BitsToDouble( - (long)((sign ? 1ul : 0ul) << 63 | (biasedExp & 0x7FFul) << 52 | (intMant & 0x000FFFFFFFFFFFFFul))); - } - - if (error != 0d) - { - SoftFloat.FPProcessException(FPException.Inexact, context); - } - - return result; - } - - private static double FPConvertNaN(ushort valueBits) - { - return BitConverter.Int64BitsToDouble( - (long)(((ulong)valueBits & 0x8000ul) << 48 | 0x7FF8000000000000ul | ((ulong)valueBits & 0x01FFul) << 42)); - } - } - - static class SoftFloat32_16 - { - [UnmanagedCallersOnly] - public static ushort FPConvert(float value) - { - ExecutionContext context = NativeInterface.GetContext(); - - double real = value.FPUnpackCv(out FPType type, out bool sign, out uint valueBits, context); - - bool altHp = (context.Fpcr & FPCR.Ahp) != 0; - - ushort resultBits; - - if (type is FPType.SNaN or FPType.QNaN) - { - if (altHp) - { - resultBits = SoftFloat16.FPZero(sign); - } - else if ((context.Fpcr & FPCR.Dn) != 0) - { - resultBits = SoftFloat16.FPDefaultNaN(); - } - else - { - resultBits = FPConvertNaN(valueBits); - } - - if (type == FPType.SNaN || altHp) - { - SoftFloat.FPProcessException(FPException.InvalidOp, context); - } - } - else if (type == FPType.Infinity) - { - if (altHp) - { - resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); - - SoftFloat.FPProcessException(FPException.InvalidOp, context); - } - else - { - resultBits = SoftFloat16.FPInfinity(sign); - } - } - else if (type == FPType.Zero) - { - resultBits = SoftFloat16.FPZero(sign); - } - else - { - resultBits = SoftFloat16.FPRoundCv(real, context); - } - - return resultBits; - } - - private static double FPUnpackCv( - this float value, - out FPType type, - out bool sign, - out uint valueBits, - ExecutionContext context) - { - valueBits = (uint)BitConverter.SingleToInt32Bits(value); - - sign = (~valueBits & 0x80000000u) == 0u; - - uint exp32 = (valueBits & 0x7F800000u) >> 23; - uint frac32 = valueBits & 0x007FFFFFu; - - double real; - - if (exp32 == 0u) - { - if (frac32 == 0u || (context.Fpcr & FPCR.Fz) != 0) - { - type = FPType.Zero; - real = 0d; - - if (frac32 != 0u) - { - SoftFloat.FPProcessException(FPException.InputDenorm, context); - } - } - else - { - type = FPType.Nonzero; // Subnormal. - real = Math.Pow(2d, -126) * ((double)frac32 * Math.Pow(2d, -23)); - } - } - else if (exp32 == 0xFFu) - { - if (frac32 == 0u) - { - type = FPType.Infinity; - real = Math.Pow(2d, 1000); - } - else - { - type = (~frac32 & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN; - real = 0d; - } - } - else - { - type = FPType.Nonzero; // Normal. - real = Math.Pow(2d, (int)exp32 - 127) * (1d + (double)frac32 * Math.Pow(2d, -23)); - } - - return sign ? -real : real; - } - - private static ushort FPConvertNaN(uint valueBits) - { - return (ushort)((valueBits & 0x80000000u) >> 16 | 0x7E00u | (valueBits & 0x003FE000u) >> 13); - } - } - - static class SoftFloat32 - { - [UnmanagedCallersOnly] - public static float FPAdd(float value1, float value2) - { - return FPAddFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPAddFpscr(float value1, float value2, byte standardFpscr) - { - return FPAddFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static float FPAddFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - 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; - - if (inf1 && inf2 && sign1 == !sign2) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if ((inf1 && !sign1) || (inf2 && !sign2)) - { - result = FPInfinity(false); - } - else if ((inf1 && sign1) || (inf2 && sign2)) - { - result = FPInfinity(true); - } - else if (zero1 && zero2 && sign1 == sign2) - { - result = FPZero(sign1); - } - else - { - result = value1 + value2; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static int FPCompare(float value1, float value2, byte signalNaNs) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - int result; - - if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) - { - result = 0b0011; - - if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs == 1) - { - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - } - else - { - if (value1 == value2) - { - result = 0b0110; - } - else if (value1 < value2) - { - result = 0b1000; - } - else - { - result = 0b0010; - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPCompareEQ(float value1, float value2) - { - return FPCompareEQFpscrImpl(value1, value2, false); - } - - private static float FPCompareEQFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - float result; - - if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) - { - result = ZerosOrOnes(false); - - if (type1 == FPType.SNaN || type2 == FPType.SNaN) - { - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - } - else - { - result = ZerosOrOnes(value1 == value2); - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPCompareEQFpscr(float value1, float value2, byte standardFpscr) - { - return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1); - } - - [UnmanagedCallersOnly] - public static float FPCompareGE(float value1, float value2) - { - return FPCompareGEFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPCompareGEFpscr(float value1, float value2, byte standardFpscr) - { - return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static float FPCompareGEFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - float result; - - if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) - { - result = ZerosOrOnes(false); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else - { - result = ZerosOrOnes(value1 >= value2); - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPCompareGT(float value1, float value2) - { - return FPCompareGTFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPCompareGTFpscr(float value1, float value2, byte standardFpscr) - { - return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static float FPCompareGTFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - float result; - - if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) - { - result = ZerosOrOnes(false); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else - { - result = ZerosOrOnes(value1 > value2); - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPCompareLE(float value1, float value2) - { - return FPCompareGEFpscrImpl(value2, value1, false); - } - - [UnmanagedCallersOnly] - public static float FPCompareLT(float value1, float value2) - { - return FPCompareGTFpscrImpl(value2, value1, false); - } - - [UnmanagedCallersOnly] - public static float FPCompareLEFpscr(float value1, float value2, byte standardFpscr) - { - return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1); - } - - [UnmanagedCallersOnly] - public static float FPCompareLTFpscr(float value1, float value2, byte standardFpscr) - { - return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1); - } - - [UnmanagedCallersOnly] - public static float FPDiv(float value1, float value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = 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); - - 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; - - if ((inf1 && inf2) || (zero1 && zero2)) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if (inf1 || zero2) - { - result = FPInfinity(sign1 ^ sign2); - - if (!inf1) - { - SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); - } - } - else if (zero1 || inf2) - { - result = FPZero(sign1 ^ sign2); - } - else - { - result = value1 / value2; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPMax(float value1, float value2) - { - return FPMaxFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPMaxFpscr(float value1, float value2, byte standardFpscr) - { - return FPMaxFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static float FPMaxFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); - - if (!done) - { - if (value1 > value2) - { - if (type1 == FPType.Infinity) - { - result = FPInfinity(sign1); - } - else if (type1 == FPType.Zero) - { - result = FPZero(sign1 && sign2); - } - else - { - result = value1; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - else - { - if (type2 == FPType.Infinity) - { - result = FPInfinity(sign2); - } - else if (type2 == FPType.Zero) - { - result = FPZero(sign1 && sign2); - } - else - { - result = value2; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPMaxNum(float value1, float value2) - { - return FPMaxNumFpscrImpl(value1, value2, false); - } - - private static float FPMaxNumFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - if (type1 == FPType.QNaN && type2 != FPType.QNaN) - { - value1 = FPInfinity(true); - } - else if (type1 != FPType.QNaN && type2 == FPType.QNaN) - { - value2 = FPInfinity(true); - } - - return FPMaxFpscrImpl(value1, value2, standardFpscr); - } - - [UnmanagedCallersOnly] - public static float FPMaxNumFpscr(float value1, float value2, byte standardFpscr) - { - return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1); - } - - [UnmanagedCallersOnly] - public static float FPMin(float value1, float value2) - { - return FPMinFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPMinFpscr(float value1, float value2, byte standardFpscr) - { - return FPMinFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static float FPMinFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); - - if (!done) - { - if (value1 < value2) - { - if (type1 == FPType.Infinity) - { - result = FPInfinity(sign1); - } - else if (type1 == FPType.Zero) - { - result = FPZero(sign1 || sign2); - } - else - { - result = value1; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - else - { - if (type2 == FPType.Infinity) - { - result = FPInfinity(sign2); - } - else if (type2 == FPType.Zero) - { - result = FPZero(sign1 || sign2); - } - else - { - result = value2; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPMinNum(float value1, float value2) - { - return FPMinNumFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPMinNumFpscr(float value1, float value2, byte standardFpscr) - { - return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static float FPMinNumFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - if (type1 == FPType.QNaN && type2 != FPType.QNaN) - { - value1 = FPInfinity(false); - } - else if (type1 != FPType.QNaN && type2 == FPType.QNaN) - { - value2 = FPInfinity(false); - } - - return FPMinFpscrImpl(value1, value2, standardFpscr); - } - - [UnmanagedCallersOnly] - public static float FPMul(float value1, float value2) - { - return FPMulFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPMulFpscr(float value1, float value2, byte standardFpscr) - { - return FPMulFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static float FPMulFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - 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; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if (inf1 || inf2) - { - result = FPInfinity(sign1 ^ sign2); - } - else if (zero1 || zero2) - { - result = FPZero(sign1 ^ sign2); - } - else - { - result = value1 * value2; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPMulAdd(float valueA, float value1, float value2) - { - return FPMulAddFpscrImpl(valueA, value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPMulAddFpscr(float valueA, float value1, float value2, byte standardFpscr) - { - return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1); - } - - private static float FPMulAddFpscrImpl(float valueA, float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - 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); - - 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); - - if (typeA == FPType.QNaN && ((inf1 && zero2) || (zero1 && inf2))) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - - if (!done) - { - bool infA = typeA == FPType.Infinity; - bool zeroA = typeA == FPType.Zero; - - bool signP = sign1 ^ sign2; - bool infP = inf1 || inf2; - bool zeroP = zero1 || zero2; - - if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if ((infA && !signA) || (infP && !signP)) - { - result = FPInfinity(false); - } - else if ((infA && signA) || (infP && signP)) - { - result = FPInfinity(true); - } - else if (zeroA && zeroP && signA == signP) - { - result = FPZero(signA); - } - else - { - result = MathF.FusedMultiplyAdd(value1, value2, valueA); - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPMulSub(float valueA, float value1, float value2) - { - value1 = value1.FPNeg(); - - return FPMulAddFpscrImpl(valueA, value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPMulSubFpscr(float valueA, float value1, float value2, byte standardFpscr) - { - value1 = value1.FPNeg(); - - return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1); - } - - [UnmanagedCallersOnly] - public static float FPMulX(float value1, float value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = 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); - - 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; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - result = FPTwo(sign1 ^ sign2); - } - else if (inf1 || inf2) - { - result = FPInfinity(sign1 ^ sign2); - } - else if (zero1 || zero2) - { - result = FPZero(sign1 ^ sign2); - } - else - { - result = value1 * value2; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPNegMulAdd(float valueA, float value1, float value2) - { - valueA = valueA.FPNeg(); - value1 = value1.FPNeg(); - - return FPMulAddFpscrImpl(valueA, value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPNegMulSub(float valueA, float value1, float value2) - { - valueA = valueA.FPNeg(); - - return FPMulAddFpscrImpl(valueA, value1, value2, false); - } - - [UnmanagedCallersOnly] - public static float FPRecipEstimate(float value) - { - return FPRecipEstimateFpscrImpl(value, false); - } - - [UnmanagedCallersOnly] - public static float FPRecipEstimateFpscr(float value, byte standardFpscr) - { - return FPRecipEstimateFpscrImpl(value, standardFpscr == 1); - } - - private static float FPRecipEstimateFpscrImpl(float value, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr); - - float result; - - if (type is FPType.SNaN or FPType.QNaN) - { - result = FPProcessNaN(type, op, context, fpcr); - } - else if (type == FPType.Infinity) - { - result = FPZero(sign); - } - else if (type == FPType.Zero) - { - result = FPInfinity(sign); - - SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); - } - else if (MathF.Abs(value) < MathF.Pow(2f, -128)) - { - bool overflowToInf = fpcr.GetRoundingMode() switch - { - FPRoundingMode.ToNearest => true, - 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); - } - else if ((fpcr & FPCR.Fz) != 0 && (MathF.Abs(value) >= MathF.Pow(2f, 126))) - { - result = FPZero(sign); - - context.Fpsr |= FPSR.Ufc; - } - else - { - ulong fraction = (ulong)(op & 0x007FFFFFu) << 29; - uint exp = (op & 0x7F800000u) >> 23; - - if (exp == 0u) - { - if ((fraction & 0x0008000000000000ul) == 0ul) - { - fraction = (fraction & 0x0003FFFFFFFFFFFFul) << 2; - exp -= 1u; - } - else - { - fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; - } - } - - uint scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44); - - uint resultExp = 253u - exp; - - uint estimate = (uint)SoftFloat.RecipEstimateTable[scaled - 256u] + 256u; - - fraction = (ulong)(estimate & 0xFFu) << 44; - - if (resultExp == 0u) - { - fraction = ((fraction & 0x000FFFFFFFFFFFFEul) | 0x0010000000000000ul) >> 1; - } - else if (resultExp + 1u == 0u) - { - fraction = ((fraction & 0x000FFFFFFFFFFFFCul) | 0x0010000000000000ul) >> 2; - resultExp = 0u; - } - - result = BitConverter.Int32BitsToSingle( - (int)((sign ? 1u : 0u) << 31 | (resultExp & 0xFFu) << 23 | (uint)(fraction >> 29) & 0x007FFFFFu)); - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPRecipStep(float value1, float value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.StandardFpcrValue; - - 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; - - float product; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - product = FPZero(false); - } - else - { - product = FPMulFpscrImpl(value1, value2, true); - } - - result = FPSubFpscrImpl(FPTwo(false), product, true); - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPRecipStepFused(float value1, float value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value1 = value1.FPNeg(); - - 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); - - 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; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - result = FPTwo(false); - } - else if (inf1 || inf2) - { - result = FPInfinity(sign1 ^ sign2); - } - else - { - result = MathF.FusedMultiplyAdd(value1, value2, 2f); - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPRecpX(float value) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr); - - float result; - - if (type is FPType.SNaN or FPType.QNaN) - { - result = FPProcessNaN(type, op, context, fpcr); - } - else - { - uint notExp = (~op >> 23) & 0xFFu; - uint maxExp = 0xFEu; - - result = BitConverter.Int32BitsToSingle( - (int)((sign ? 1u : 0u) << 31 | (notExp == 0xFFu ? maxExp : notExp) << 23)); - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPRSqrtEstimate(float value) - { - return FPRSqrtEstimateFpscrImpl(value, false); - } - - [UnmanagedCallersOnly] - public static float FPRSqrtEstimateFpscr(float value, byte standardFpscr) - { - return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1); - } - - private static float FPRSqrtEstimateFpscrImpl(float value, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr); - - float result; - - if (type is FPType.SNaN or FPType.QNaN) - { - result = FPProcessNaN(type, op, context, fpcr); - } - else if (type == FPType.Zero) - { - result = FPInfinity(sign); - - SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); - } - else if (sign) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if (type == FPType.Infinity) - { - result = FPZero(false); - } - else - { - ulong fraction = (ulong)(op & 0x007FFFFFu) << 29; - uint exp = (op & 0x7F800000u) >> 23; - - if (exp == 0u) - { - while ((fraction & 0x0008000000000000ul) == 0ul) - { - fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; - exp -= 1u; - } - - fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; - } - - uint scaled; - - if ((exp & 1u) == 0u) - { - scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44); - } - else - { - scaled = (uint)(((fraction & 0x000FE00000000000ul) | 0x0010000000000000ul) >> 45); - } - - uint resultExp = (380u - exp) >> 1; - - uint estimate = (uint)SoftFloat.RecipSqrtEstimateTable[scaled - 128u] + 256u; - - result = BitConverter.Int32BitsToSingle((int)((resultExp & 0xFFu) << 23 | (estimate & 0xFFu) << 15)); - } - - return result; - } - - public static float FPHalvedSub(float value1, float value2, ExecutionContext context, FPCR 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); - - 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; - - if (inf1 && inf2 && sign1 == sign2) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if ((inf1 && !sign1) || (inf2 && sign2)) - { - result = FPInfinity(false); - } - else if ((inf1 && sign1) || (inf2 && !sign2)) - { - result = FPInfinity(true); - } - else if (zero1 && zero2 && sign1 == !sign2) - { - result = FPZero(sign1); - } - else - { - result = (value1 - value2) / 2.0f; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPRSqrtStep(float value1, float value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.StandardFpcrValue; - - 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; - - float product; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - product = FPZero(false); - } - else - { - product = FPMulFpscrImpl(value1, value2, true); - } - - result = FPHalvedSub(FPThree(false), product, context, fpcr); - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPRSqrtStepFused(float value1, float value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value1 = value1.FPNeg(); - - 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); - - 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; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - result = FPOnePointFive(false); - } - else if (inf1 || inf2) - { - result = FPInfinity(sign1 ^ sign2); - } - else - { - result = MathF.FusedMultiplyAdd(value1, value2, 3f) / 2f; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPSqrt(float value) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value = value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr); - - float result; - - if (type is FPType.SNaN or FPType.QNaN) - { - result = FPProcessNaN(type, op, context, fpcr); - } - else if (type == FPType.Zero) - { - result = FPZero(sign); - } - else if (type == FPType.Infinity && !sign) - { - result = FPInfinity(sign); - } - else if (sign) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else - { - result = MathF.Sqrt(value); - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static float FPSub(float value1, float value2) - { - return FPSubFpscrImpl(value1, value2, false); - } - - private static float FPSubFpscrImpl(float value1, float value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - 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; - - if (inf1 && inf2 && sign1 == sign2) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if ((inf1 && !sign1) || (inf2 && sign2)) - { - result = FPInfinity(false); - } - else if ((inf1 && sign1) || (inf2 && !sign2)) - { - result = FPInfinity(true); - } - else if (zero1 && zero2 && sign1 == !sign2) - { - result = FPZero(sign1); - } - else - { - result = value1 - value2; - - if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0f); - } - } - } - - return result; - } - - public static float FPDefaultNaN() - { - return BitConverter.Int32BitsToSingle(0x7fc00000); - } - - public static float FPInfinity(bool sign) - { - return sign ? float.NegativeInfinity : float.PositiveInfinity; - } - - public static float FPZero(bool sign) - { - return sign ? -0f : +0f; - } - - public static float FPMaxNormal(bool sign) - { - return sign ? float.MinValue : float.MaxValue; - } - - private static float FPTwo(bool sign) - { - return sign ? -2f : +2f; - } - - private static float FPThree(bool sign) - { - return sign ? -3f : +3f; - } - - private static float FPOnePointFive(bool sign) - { - return sign ? -1.5f : +1.5f; - } - - private static float FPNeg(this float value) - { - return -value; - } - - private static float ZerosOrOnes(bool ones) - { - return BitConverter.Int32BitsToSingle(ones ? -1 : 0); - } - - private static float FPUnpack( - this float value, - out FPType type, - out bool sign, - out uint valueBits, - ExecutionContext context, - FPCR fpcr) - { - valueBits = (uint)BitConverter.SingleToInt32Bits(value); - - sign = (~valueBits & 0x80000000u) == 0u; - - if ((valueBits & 0x7F800000u) == 0u) - { - if ((valueBits & 0x007FFFFFu) == 0u || (fpcr & FPCR.Fz) != 0) - { - type = FPType.Zero; - value = FPZero(sign); - - if ((valueBits & 0x007FFFFFu) != 0u) - { - SoftFloat.FPProcessException(FPException.InputDenorm, context, fpcr); - } - } - else - { - type = FPType.Nonzero; - } - } - else if ((~valueBits & 0x7F800000u) == 0u) - { - if ((valueBits & 0x007FFFFFu) == 0u) - { - type = FPType.Infinity; - } - else - { - type = (~valueBits & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN; - value = FPZero(sign); - } - } - else - { - type = FPType.Nonzero; - } - - return value; - } - - private static float FPProcessNaNs( - FPType type1, - FPType type2, - uint op1, - uint op2, - out bool done, - ExecutionContext context, - FPCR fpcr) - { - done = true; - - if (type1 == FPType.SNaN) - { - return FPProcessNaN(type1, op1, context, fpcr); - } - else if (type2 == FPType.SNaN) - { - return FPProcessNaN(type2, op2, context, fpcr); - } - else if (type1 == FPType.QNaN) - { - return FPProcessNaN(type1, op1, context, fpcr); - } - else if (type2 == FPType.QNaN) - { - return FPProcessNaN(type2, op2, context, fpcr); - } - - done = false; - - return FPZero(false); - } - - private static float FPProcessNaNs3( - FPType type1, - FPType type2, - FPType type3, - uint op1, - uint op2, - uint op3, - out bool done, - ExecutionContext context, - FPCR fpcr) - { - done = true; - - if (type1 == FPType.SNaN) - { - return FPProcessNaN(type1, op1, context, fpcr); - } - else if (type2 == FPType.SNaN) - { - return FPProcessNaN(type2, op2, context, fpcr); - } - else if (type3 == FPType.SNaN) - { - return FPProcessNaN(type3, op3, context, fpcr); - } - else if (type1 == FPType.QNaN) - { - return FPProcessNaN(type1, op1, context, fpcr); - } - else if (type2 == FPType.QNaN) - { - return FPProcessNaN(type2, op2, context, fpcr); - } - else if (type3 == FPType.QNaN) - { - return FPProcessNaN(type3, op3, context, fpcr); - } - - done = false; - - return FPZero(false); - } - - private static float FPProcessNaN(FPType type, uint op, ExecutionContext context, FPCR fpcr) - { - if (type == FPType.SNaN) - { - op |= 1u << 22; - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - - if ((fpcr & FPCR.Dn) != 0) - { - return FPDefaultNaN(); - } - - return BitConverter.Int32BitsToSingle((int)op); - } - } - - static class SoftFloat64_16 - { - [UnmanagedCallersOnly] - public static ushort FPConvert(double value) - { - ExecutionContext context = NativeInterface.GetContext(); - - double real = value.FPUnpackCv(out FPType type, out bool sign, out ulong valueBits, context); - - bool altHp = (context.Fpcr & FPCR.Ahp) != 0; - - ushort resultBits; - - if (type is FPType.SNaN or FPType.QNaN) - { - if (altHp) - { - resultBits = SoftFloat16.FPZero(sign); - } - else if ((context.Fpcr & FPCR.Dn) != 0) - { - resultBits = SoftFloat16.FPDefaultNaN(); - } - else - { - resultBits = FPConvertNaN(valueBits); - } - - if (type == FPType.SNaN || altHp) - { - SoftFloat.FPProcessException(FPException.InvalidOp, context); - } - } - else if (type == FPType.Infinity) - { - if (altHp) - { - resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); - - SoftFloat.FPProcessException(FPException.InvalidOp, context); - } - else - { - resultBits = SoftFloat16.FPInfinity(sign); - } - } - else if (type == FPType.Zero) - { - resultBits = SoftFloat16.FPZero(sign); - } - else - { - resultBits = SoftFloat16.FPRoundCv(real, context); - } - - return resultBits; - } - - private static double FPUnpackCv( - this double value, - out FPType type, - out bool sign, - out ulong valueBits, - ExecutionContext context) - { - valueBits = (ulong)BitConverter.DoubleToInt64Bits(value); - - sign = (~valueBits & 0x8000000000000000ul) == 0u; - - ulong exp64 = (valueBits & 0x7FF0000000000000ul) >> 52; - ulong frac64 = valueBits & 0x000FFFFFFFFFFFFFul; - - double real; - - if (exp64 == 0u) - { - if (frac64 == 0u || (context.Fpcr & FPCR.Fz) != 0) - { - type = FPType.Zero; - real = 0d; - - if (frac64 != 0u) - { - SoftFloat.FPProcessException(FPException.InputDenorm, context); - } - } - else - { - type = FPType.Nonzero; // Subnormal. - real = Math.Pow(2d, -1022) * ((double)frac64 * Math.Pow(2d, -52)); - } - } - else if (exp64 == 0x7FFul) - { - if (frac64 == 0u) - { - type = FPType.Infinity; - real = Math.Pow(2d, 1000000); - } - else - { - type = (~frac64 & 0x0008000000000000ul) == 0u ? FPType.QNaN : FPType.SNaN; - real = 0d; - } - } - else - { - type = FPType.Nonzero; // Normal. - real = Math.Pow(2d, (int)exp64 - 1023) * (1d + (double)frac64 * Math.Pow(2d, -52)); - } - - return sign ? -real : real; - } - - private static ushort FPConvertNaN(ulong valueBits) - { - return (ushort)((valueBits & 0x8000000000000000ul) >> 48 | 0x7E00u | (valueBits & 0x0007FC0000000000ul) >> 42); - } - } - - static class SoftFloat64 - { - [UnmanagedCallersOnly] - public static double FPAdd(double value1, double value2) - { - return FPAddFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPAddFpscr(double value1, double value2, byte standardFpscr) - { - return FPAddFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPAddFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - 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; - - if (inf1 && inf2 && sign1 == !sign2) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if ((inf1 && !sign1) || (inf2 && !sign2)) - { - result = FPInfinity(false); - } - else if ((inf1 && sign1) || (inf2 && sign2)) - { - result = FPInfinity(true); - } - else if (zero1 && zero2 && sign1 == sign2) - { - result = FPZero(sign1); - } - else - { - result = value1 + value2; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static int FPCompare(double value1, double value2, byte signalNaNs) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - int result; - - if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) - { - result = 0b0011; - - if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs == 1) - { - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - } - else - { - if (value1 == value2) - { - result = 0b0110; - } - else if (value1 < value2) - { - result = 0b1000; - } - else - { - result = 0b0010; - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPCompareEQ(double value1, double value2) - { - return FPCompareEQFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPCompareEQFpscr(double value1, double value2, byte standardFpscr) - { - return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPCompareEQFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - double result; - - if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) - { - result = ZerosOrOnes(false); - - if (type1 == FPType.SNaN || type2 == FPType.SNaN) - { - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - } - else - { - result = ZerosOrOnes(value1 == value2); - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPCompareGE(double value1, double value2) - { - return FPCompareGEFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPCompareGEFpscr(double value1, double value2, byte standardFpscr) - { - return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPCompareGEFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - double result; - - if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) - { - result = ZerosOrOnes(false); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else - { - result = ZerosOrOnes(value1 >= value2); - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPCompareGT(double value1, double value2) - { - return FPCompareGTFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPCompareGTFpscr(double value1, double value2, byte standardFpscr) - { - return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPCompareGTFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - double result; - - if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) - { - result = ZerosOrOnes(false); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else - { - result = ZerosOrOnes(value1 > value2); - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPCompareLE(double value1, double value2) - { - return FPCompareGEFpscrImpl(value2, value1, false); - } - - [UnmanagedCallersOnly] - public static double FPCompareLT(double value1, double value2) - { - return FPCompareGTFpscrImpl(value2, value1, false); - } - - [UnmanagedCallersOnly] - public static double FPCompareLEFpscr(double value1, double value2, byte standardFpscr) - { - return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1); - } - - [UnmanagedCallersOnly] - public static double FPCompareLTFpscr(double value1, double value2, byte standardFpscr) - { - return FPCompareGTFpscrImpl(value2, value1, standardFpscr == 1); - } - - [UnmanagedCallersOnly] - public static double FPDiv(double value1, double value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = 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); - - 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; - - if ((inf1 && inf2) || (zero1 && zero2)) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if (inf1 || zero2) - { - result = FPInfinity(sign1 ^ sign2); - - if (!inf1) - { - SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); - } - } - else if (zero1 || inf2) - { - result = FPZero(sign1 ^ sign2); - } - else - { - result = value1 / value2; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPMax(double value1, double value2) - { - return FPMaxFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPMaxFpscr(double value1, double value2, byte standardFpscr) - { - return FPMaxFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPMaxFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); - - if (!done) - { - if (value1 > value2) - { - if (type1 == FPType.Infinity) - { - result = FPInfinity(sign1); - } - else if (type1 == FPType.Zero) - { - result = FPZero(sign1 && sign2); - } - else - { - result = value1; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - else - { - if (type2 == FPType.Infinity) - { - result = FPInfinity(sign2); - } - else if (type2 == FPType.Zero) - { - result = FPZero(sign1 && sign2); - } - else - { - result = value2; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPMaxNum(double value1, double value2) - { - return FPMaxNumFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPMaxNumFpscr(double value1, double value2, byte standardFpscr) - { - return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPMaxNumFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - if (type1 == FPType.QNaN && type2 != FPType.QNaN) - { - value1 = FPInfinity(true); - } - else if (type1 != FPType.QNaN && type2 == FPType.QNaN) - { - value2 = FPInfinity(true); - } - - return FPMaxFpscrImpl(value1, value2, standardFpscr); - } - - [UnmanagedCallersOnly] - public static double FPMin(double value1, double value2) - { - return FPMinFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPMinFpscr(double value1, double value2, byte standardFpscr) - { - return FPMinFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPMinFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); - - if (!done) - { - if (value1 < value2) - { - if (type1 == FPType.Infinity) - { - result = FPInfinity(sign1); - } - else if (type1 == FPType.Zero) - { - result = FPZero(sign1 || sign2); - } - else - { - result = value1; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - else - { - if (type2 == FPType.Infinity) - { - result = FPInfinity(sign2); - } - else if (type2 == FPType.Zero) - { - result = FPZero(sign1 || sign2); - } - else - { - result = value2; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPMinNum(double value1, double value2) - { - return FPMinNumFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPMinNumFpscr(double value1, double value2, byte standardFpscr) - { - return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPMinNumFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); - value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); - - if (type1 == FPType.QNaN && type2 != FPType.QNaN) - { - value1 = FPInfinity(false); - } - else if (type1 != FPType.QNaN && type2 == FPType.QNaN) - { - value2 = FPInfinity(false); - } - - return FPMinFpscrImpl(value1, value2, standardFpscr); - } - - [UnmanagedCallersOnly] - public static double FPMul(double value1, double value2) - { - return FPMulFpscrImpl(value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPMulFpscr(double value1, double value2, byte standardFpscr) - { - return FPMulFpscrImpl(value1, value2, standardFpscr == 1); - } - - private static double FPMulFpscrImpl(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - 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; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if (inf1 || inf2) - { - result = FPInfinity(sign1 ^ sign2); - } - else if (zero1 || zero2) - { - result = FPZero(sign1 ^ sign2); - } - else - { - result = value1 * value2; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPMulAdd(double valueA, double value1, double value2) - { - return FPMulAddFpscrImpl(valueA, value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPMulAddFpscr(double valueA, double value1, double value2, byte standardFpscr) - { - return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1); - } - - private static double FPMulAddFpscrImpl(double valueA, double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - 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); - - 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); - - if (typeA == FPType.QNaN && ((inf1 && zero2) || (zero1 && inf2))) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - - if (!done) - { - bool infA = typeA == FPType.Infinity; - bool zeroA = typeA == FPType.Zero; - - bool signP = sign1 ^ sign2; - bool infP = inf1 || inf2; - bool zeroP = zero1 || zero2; - - if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if ((infA && !signA) || (infP && !signP)) - { - result = FPInfinity(false); - } - else if ((infA && signA) || (infP && signP)) - { - result = FPInfinity(true); - } - else if (zeroA && zeroP && signA == signP) - { - result = FPZero(signA); - } - else - { - result = Math.FusedMultiplyAdd(value1, value2, valueA); - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPMulSub(double valueA, double value1, double value2) - { - value1 = value1.FPNeg(); - - return FPMulAddFpscrImpl(valueA, value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPMulSubFpscr(double valueA, double value1, double value2, byte standardFpscr) - { - value1 = value1.FPNeg(); - - return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1); - } - - [UnmanagedCallersOnly] - public static double FPMulX(double value1, double value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = 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); - - 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; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - result = FPTwo(sign1 ^ sign2); - } - else if (inf1 || inf2) - { - result = FPInfinity(sign1 ^ sign2); - } - else if (zero1 || zero2) - { - result = FPZero(sign1 ^ sign2); - } - else - { - result = value1 * value2; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPNegMulAdd(double valueA, double value1, double value2) - { - valueA = valueA.FPNeg(); - value1 = value1.FPNeg(); - - return FPMulAddFpscrImpl(valueA, value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPNegMulSub(double valueA, double value1, double value2) - { - valueA = valueA.FPNeg(); - - return FPMulAddFpscrImpl(valueA, value1, value2, false); - } - - [UnmanagedCallersOnly] - public static double FPRecipEstimate(double value) - { - return FPRecipEstimateFpscrImpl(value, false); - } - - [UnmanagedCallersOnly] - public static double FPRecipEstimateFpscr(double value, byte standardFpscr) - { - return FPRecipEstimateFpscrImpl(value, standardFpscr == 1); - } - - private static double FPRecipEstimateFpscrImpl(double value, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr); - - double result; - - if (type is FPType.SNaN or FPType.QNaN) - { - result = FPProcessNaN(type, op, context, fpcr); - } - else if (type == FPType.Infinity) - { - result = FPZero(sign); - } - else if (type == FPType.Zero) - { - result = FPInfinity(sign); - - SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); - } - else if (Math.Abs(value) < Math.Pow(2d, -1024)) - { - bool overflowToInf = fpcr.GetRoundingMode() switch - { - FPRoundingMode.ToNearest => true, - 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); - } - else if ((fpcr & FPCR.Fz) != 0 && (Math.Abs(value) >= Math.Pow(2d, 1022))) - { - result = FPZero(sign); - - context.Fpsr |= FPSR.Ufc; - } - else - { - ulong fraction = op & 0x000FFFFFFFFFFFFFul; - uint exp = (uint)((op & 0x7FF0000000000000ul) >> 52); - - if (exp == 0u) - { - if ((fraction & 0x0008000000000000ul) == 0ul) - { - fraction = (fraction & 0x0003FFFFFFFFFFFFul) << 2; - exp -= 1u; - } - else - { - fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; - } - } - - uint scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44); - - uint resultExp = 2045u - exp; - - uint estimate = (uint)SoftFloat.RecipEstimateTable[scaled - 256u] + 256u; - - fraction = (ulong)(estimate & 0xFFu) << 44; - - if (resultExp == 0u) - { - fraction = ((fraction & 0x000FFFFFFFFFFFFEul) | 0x0010000000000000ul) >> 1; - } - else if (resultExp + 1u == 0u) - { - fraction = ((fraction & 0x000FFFFFFFFFFFFCul) | 0x0010000000000000ul) >> 2; - resultExp = 0u; - } - - result = BitConverter.Int64BitsToDouble( - (long)((sign ? 1ul : 0ul) << 63 | (resultExp & 0x7FFul) << 52 | (fraction & 0x000FFFFFFFFFFFFFul))); - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPRecipStep(double value1, double value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.StandardFpcrValue; - - 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; - - double product; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - product = FPZero(false); - } - else - { - product = FPMulFpscrImpl(value1, value2, true); - } - - result = FPSubFpscr(FPTwo(false), product, true); - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPRecipStepFused(double value1, double value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value1 = value1.FPNeg(); - - 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); - - 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; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - result = FPTwo(false); - } - else if (inf1 || inf2) - { - result = FPInfinity(sign1 ^ sign2); - } - else - { - result = Math.FusedMultiplyAdd(value1, value2, 2d); - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPRecpX(double value) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr); - - double result; - - if (type is FPType.SNaN or FPType.QNaN) - { - result = FPProcessNaN(type, op, context, fpcr); - } - else - { - ulong notExp = (~op >> 52) & 0x7FFul; - ulong maxExp = 0x7FEul; - - result = BitConverter.Int64BitsToDouble( - (long)((sign ? 1ul : 0ul) << 63 | (notExp == 0x7FFul ? maxExp : notExp) << 52)); - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPRSqrtEstimate(double value) - { - return FPRSqrtEstimateFpscrImpl(value, false); - } - - [UnmanagedCallersOnly] - public static double FPRSqrtEstimateFpscr(double value, byte standardFpscr) - { - return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1); - } - - private static double FPRSqrtEstimateFpscrImpl(double value, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; - - value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr); - - double result; - - if (type is FPType.SNaN or FPType.QNaN) - { - result = FPProcessNaN(type, op, context, fpcr); - } - else if (type == FPType.Zero) - { - result = FPInfinity(sign); - - SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); - } - else if (sign) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if (type == FPType.Infinity) - { - result = FPZero(false); - } - else - { - ulong fraction = op & 0x000FFFFFFFFFFFFFul; - uint exp = (uint)((op & 0x7FF0000000000000ul) >> 52); - - if (exp == 0u) - { - while ((fraction & 0x0008000000000000ul) == 0ul) - { - fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; - exp -= 1u; - } - - fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; - } - - uint scaled; - - if ((exp & 1u) == 0u) - { - scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44); - } - else - { - scaled = (uint)(((fraction & 0x000FE00000000000ul) | 0x0010000000000000ul) >> 45); - } - - uint resultExp = (3068u - exp) >> 1; - - uint estimate = (uint)SoftFloat.RecipSqrtEstimateTable[scaled - 128u] + 256u; - - result = BitConverter.Int64BitsToDouble((long)((resultExp & 0x7FFul) << 52 | (estimate & 0xFFul) << 44)); - } - - return result; - } - - public static double FPHalvedSub(double value1, double value2, ExecutionContext context, FPCR 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); - - 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; - - if (inf1 && inf2 && sign1 == sign2) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if ((inf1 && !sign1) || (inf2 && sign2)) - { - result = FPInfinity(false); - } - else if ((inf1 && sign1) || (inf2 && !sign2)) - { - result = FPInfinity(true); - } - else if (zero1 && zero2 && sign1 == !sign2) - { - result = FPZero(sign1); - } - else - { - result = (value1 - value2) / 2.0; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPRSqrtStep(double value1, double value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.StandardFpcrValue; - - 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; - - double product; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - product = FPZero(false); - } - else - { - product = FPMulFpscrImpl(value1, value2, true); - } - - result = FPHalvedSub(FPThree(false), product, context, fpcr); - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPRSqrtStepFused(double value1, double value2) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value1 = value1.FPNeg(); - - 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); - - 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; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - result = FPOnePointFive(false); - } - else if (inf1 || inf2) - { - result = FPInfinity(sign1 ^ sign2); - } - else - { - result = Math.FusedMultiplyAdd(value1, value2, 3d) / 2d; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPSqrt(double value) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; - - value = value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr); - - double result; - - if (type is FPType.SNaN or FPType.QNaN) - { - result = FPProcessNaN(type, op, context, fpcr); - } - else if (type == FPType.Zero) - { - result = FPZero(sign); - } - else if (type == FPType.Infinity && !sign) - { - result = FPInfinity(sign); - } - else if (sign) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else - { - result = Math.Sqrt(value); - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - - return result; - } - - [UnmanagedCallersOnly] - public static double FPSub(double value1, double value2) - { - return FPSubFpscr(value1, value2, false); - } - - public static double FPSubFpscr(double value1, double value2, bool standardFpscr) - { - ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); - - 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; - - if (inf1 && inf2 && sign1 == sign2) - { - result = FPDefaultNaN(); - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - else if ((inf1 && !sign1) || (inf2 && sign2)) - { - result = FPInfinity(false); - } - else if ((inf1 && sign1) || (inf2 && !sign2)) - { - result = FPInfinity(true); - } - else if (zero1 && zero2 && sign1 == !sign2) - { - result = FPZero(sign1); - } - else - { - result = value1 - value2; - - if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) - { - context.Fpsr |= FPSR.Ufc; - - result = FPZero(result < 0d); - } - } - } - - return result; - } - - public static double FPDefaultNaN() - { - return BitConverter.Int64BitsToDouble(0x7ff8000000000000); - } - - public static double FPInfinity(bool sign) - { - return sign ? double.NegativeInfinity : double.PositiveInfinity; - } - - public static double FPZero(bool sign) - { - return sign ? -0d : +0d; - } - - public static double FPMaxNormal(bool sign) - { - return sign ? double.MinValue : double.MaxValue; - } - - private static double FPTwo(bool sign) - { - return sign ? -2d : +2d; - } - - private static double FPThree(bool sign) - { - return sign ? -3d : +3d; - } - - private static double FPOnePointFive(bool sign) - { - return sign ? -1.5d : +1.5d; - } - - private static double FPNeg(this double value) - { - return -value; - } - - private static double ZerosOrOnes(bool ones) - { - return BitConverter.Int64BitsToDouble(ones ? -1L : 0L); - } - - private static double FPUnpack( - this double value, - out FPType type, - out bool sign, - out ulong valueBits, - ExecutionContext context, - FPCR fpcr) - { - valueBits = (ulong)BitConverter.DoubleToInt64Bits(value); - - sign = (~valueBits & 0x8000000000000000ul) == 0ul; - - if ((valueBits & 0x7FF0000000000000ul) == 0ul) - { - if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul || (fpcr & FPCR.Fz) != 0) - { - type = FPType.Zero; - value = FPZero(sign); - - if ((valueBits & 0x000FFFFFFFFFFFFFul) != 0ul) - { - SoftFloat.FPProcessException(FPException.InputDenorm, context, fpcr); - } - } - else - { - type = FPType.Nonzero; - } - } - else if ((~valueBits & 0x7FF0000000000000ul) == 0ul) - { - if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul) - { - type = FPType.Infinity; - } - else - { - type = (~valueBits & 0x0008000000000000ul) == 0ul ? FPType.QNaN : FPType.SNaN; - value = FPZero(sign); - } - } - else - { - type = FPType.Nonzero; - } - - return value; - } - - private static double FPProcessNaNs( - FPType type1, - FPType type2, - ulong op1, - ulong op2, - out bool done, - ExecutionContext context, - FPCR fpcr) - { - done = true; - - if (type1 == FPType.SNaN) - { - return FPProcessNaN(type1, op1, context, fpcr); - } - else if (type2 == FPType.SNaN) - { - return FPProcessNaN(type2, op2, context, fpcr); - } - else if (type1 == FPType.QNaN) - { - return FPProcessNaN(type1, op1, context, fpcr); - } - else if (type2 == FPType.QNaN) - { - return FPProcessNaN(type2, op2, context, fpcr); - } - - done = false; - - return FPZero(false); - } - - private static double FPProcessNaNs3( - FPType type1, - FPType type2, - FPType type3, - ulong op1, - ulong op2, - ulong op3, - out bool done, - ExecutionContext context, - FPCR fpcr) - { - done = true; - - if (type1 == FPType.SNaN) - { - return FPProcessNaN(type1, op1, context, fpcr); - } - else if (type2 == FPType.SNaN) - { - return FPProcessNaN(type2, op2, context, fpcr); - } - else if (type3 == FPType.SNaN) - { - return FPProcessNaN(type3, op3, context, fpcr); - } - else if (type1 == FPType.QNaN) - { - return FPProcessNaN(type1, op1, context, fpcr); - } - else if (type2 == FPType.QNaN) - { - return FPProcessNaN(type2, op2, context, fpcr); - } - else if (type3 == FPType.QNaN) - { - return FPProcessNaN(type3, op3, context, fpcr); - } - - done = false; - - return FPZero(false); - } - - private static double FPProcessNaN(FPType type, ulong op, ExecutionContext context, FPCR fpcr) - { - if (type == FPType.SNaN) - { - op |= 1ul << 51; - - SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); - } - - if ((fpcr & FPCR.Dn) != 0) - { - return FPDefaultNaN(); - } - - return BitConverter.Int64BitsToDouble((long)op); - } - } -} diff --git a/src/ARMeilleure/Instructions/SoftFloat/SoftFloat.cs b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat.cs new file mode 100644 index 000000000..366dd543e --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat.cs @@ -0,0 +1,111 @@ +using ARMeilleure.State; +using System; +using System.Diagnostics; + +namespace ARMeilleure.Instructions +{ + static class SoftFloat + { + static SoftFloat() + { + RecipEstimateTable = BuildRecipEstimateTable(); + RecipSqrtEstimateTable = BuildRecipSqrtEstimateTable(); + } + + public static readonly byte[] RecipEstimateTable; + public static readonly byte[] RecipSqrtEstimateTable; + + private static byte[] BuildRecipEstimateTable() + { + byte[] tbl = new byte[256]; + + for (int idx = 0; idx < 256; idx++) + { + uint src = (uint)idx + 256u; + + Debug.Assert(src is >= 256u and < 512u); + + src = (src << 1) + 1u; + + uint aux = (1u << 19) / src; + + uint dst = (aux + 1u) >> 1; + + Debug.Assert(dst is >= 256u and < 512u); + + tbl[idx] = (byte)(dst - 256u); + } + + return tbl; + } + + private static byte[] BuildRecipSqrtEstimateTable() + { + byte[] tbl = new byte[384]; + + for (int idx = 0; idx < 384; idx++) + { + uint src = (uint)idx + 128u; + + Debug.Assert(src is >= 128u and < 512u); + + if (src < 256u) + { + src = (src << 1) + 1u; + } + else + { + src = (src >> 1) << 1; + src = (src + 1u) << 1; + } + + uint aux = 512u; + + while (src * (aux + 1u) * (aux + 1u) < (1u << 28)) + { + aux++; + } + + uint dst = (aux + 1u) >> 1; + + Debug.Assert(dst is >= 256u and < 512u); + + tbl[idx] = (byte)(dst - 256u); + } + + return tbl; + } + + public static void FPProcessException(FPException exc, ExecutionContext context) + { + FPProcessException(exc, context, context.Fpcr); + } + + public static void FPProcessException(FPException exc, ExecutionContext context, FPCR fpcr) + { + int enable = (int)exc + 8; + + if ((fpcr & (FPCR)(1 << enable)) != 0) + { + throw new NotImplementedException("Floating-point trap handling."); + } + else + { + context.Fpsr |= (FPSR)(1 << (int)exc); + } + } + + extension(FPCR fpcr) + { + public FPRoundingMode RoundingMode + { + get + { + const int RModeShift = 22; + + return (FPRoundingMode)(((uint)fpcr >> RModeShift) & 3u); + } + } + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16.cs b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16.cs new file mode 100644 index 000000000..4038aceb6 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16.cs @@ -0,0 +1,212 @@ +using ARMeilleure.State; +using System; + +namespace ARMeilleure.Instructions +{ + static class SoftFloat16 + { + public static ushort FPDefaultNaN() + { + return (ushort)0x7E00u; + } + + public static ushort FPInfinity(bool sign) + { + return sign ? (ushort)0xFC00u : (ushort)0x7C00u; + } + + public static ushort FPZero(bool sign) + { + return sign ? (ushort)0x8000u : (ushort)0x0000u; + } + + public static ushort FPMaxNormal(bool sign) + { + return sign ? (ushort)0xFBFFu : (ushort)0x7BFFu; + } + + public static double FPUnpackCv( + this ushort valueBits, + out FPType type, + out bool sign, + ExecutionContext context) + { + sign = (~(uint)valueBits & 0x8000u) == 0u; + + uint exp16 = ((uint)valueBits & 0x7C00u) >> 10; + uint frac16 = (uint)valueBits & 0x03FFu; + + double real; + + if (exp16 == 0u) + { + if (frac16 == 0u) + { + type = FPType.Zero; + real = 0d; + } + else + { + type = FPType.Nonzero; // Subnormal. + real = Math.Pow(2d, -14) * ((double)frac16 * Math.Pow(2d, -10)); + } + } + else if (exp16 == 0x1Fu && (context.Fpcr & FPCR.Ahp) == 0) + { + if (frac16 == 0u) + { + type = FPType.Infinity; + real = Math.Pow(2d, 1000); + } + else + { + type = (~frac16 & 0x0200u) == 0u ? FPType.QNaN : FPType.SNaN; + real = 0d; + } + } + else + { + type = FPType.Nonzero; // Normal. + real = Math.Pow(2d, (int)exp16 - 15) * (1d + (double)frac16 * Math.Pow(2d, -10)); + } + + return sign ? -real : real; + } + + public static ushort FPRoundCv(double real, ExecutionContext context) + { + const int MinimumExp = -14; + + const int E = 5; + const int F = 10; + + bool sign; + double mantissa; + + if (real < 0d) + { + sign = true; + mantissa = -real; + } + else + { + sign = false; + mantissa = real; + } + + int exponent = 0; + + while (mantissa < 1d) + { + mantissa *= 2d; + exponent--; + } + + while (mantissa >= 2d) + { + mantissa /= 2d; + exponent++; + } + + uint biasedExp = (uint)Math.Max(exponent - MinimumExp + 1, 0); + + if (biasedExp == 0u) + { + 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; + + if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) + { + SoftFloat.FPProcessException(FPException.Underflow, context); + } + + bool overflowToInf; + bool roundUp; + + switch (context.Fpcr.RoundingMode) + { + case FPRoundingMode.ToNearest: + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + overflowToInf = true; + break; + + case FPRoundingMode.TowardsPlusInfinity: + roundUp = (error != 0d && !sign); + overflowToInf = !sign; + break; + + case FPRoundingMode.TowardsMinusInfinity: + roundUp = (error != 0d && sign); + overflowToInf = sign; + break; + + case FPRoundingMode.TowardsZero: + roundUp = false; + overflowToInf = false; + break; + + default: + throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.RoundingMode}\"."); + } + + if (roundUp) + { + intMant++; + + if (intMant == 1u << F) + { + biasedExp = 1u; + } + + if (intMant == 1u << (F + 1)) + { + biasedExp++; + intMant >>= 1; + } + } + + ushort resultBits; + + if ((context.Fpcr & FPCR.Ahp) == 0) + { + if (biasedExp >= (1u << E) - 1u) + { + resultBits = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); + + SoftFloat.FPProcessException(FPException.Overflow, context); + + error = 1d; + } + else + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu)); + } + } + else + { + if (biasedExp >= 1u << E) + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); + + SoftFloat.FPProcessException(FPException.InvalidOp, context); + + error = 0d; + } + else + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu)); + } + } + + if (error != 0d) + { + SoftFloat.FPProcessException(FPException.Inexact, context); + } + + return resultBits; + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16_32.cs b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16_32.cs new file mode 100644 index 000000000..f2d0f1eb4 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16_32.cs @@ -0,0 +1,182 @@ +using ARMeilleure.State; +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static class SoftFloat16_32 + { + [UnmanagedCallersOnly] + public static float FPConvert(ushort valueBits) + { + ExecutionContext context = NativeInterface.GetContext(); + + double real = valueBits.FPUnpackCv(out FPType type, out bool sign, context); + + float result; + + if (type is FPType.SNaN or FPType.QNaN) + { + if ((context.Fpcr & FPCR.Dn) != 0) + { + result = SoftFloat32.FPDefaultNaN(); + } + else + { + result = FPConvertNaN(valueBits); + } + + if (type == FPType.SNaN) + { + SoftFloat.FPProcessException(FPException.InvalidOp, context); + } + } + else if (type == FPType.Infinity) + { + result = SoftFloat32.FPInfinity(sign); + } + else if (type == FPType.Zero) + { + result = SoftFloat32.FPZero(sign); + } + else + { + result = FPRoundCv(real, context); + } + + return result; + } + + private static float FPRoundCv(double real, ExecutionContext context) + { + const int MinimumExp = -126; + + const int E = 8; + const int F = 23; + + bool sign; + double mantissa; + + if (real < 0d) + { + sign = true; + mantissa = -real; + } + else + { + sign = false; + mantissa = real; + } + + int exponent = 0; + + while (mantissa < 1d) + { + mantissa *= 2d; + exponent--; + } + + while (mantissa >= 2d) + { + mantissa /= 2d; + exponent++; + } + + 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); + + if (biasedExp == 0u) + { + 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; + + if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) + { + SoftFloat.FPProcessException(FPException.Underflow, context); + } + + bool overflowToInf; + bool roundUp; + + switch (context.Fpcr.RoundingMode) + { + case FPRoundingMode.ToNearest: + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + overflowToInf = true; + break; + + case FPRoundingMode.TowardsPlusInfinity: + roundUp = (error != 0d && !sign); + overflowToInf = !sign; + break; + + case FPRoundingMode.TowardsMinusInfinity: + roundUp = (error != 0d && sign); + overflowToInf = sign; + break; + + case FPRoundingMode.TowardsZero: + roundUp = false; + overflowToInf = false; + break; + + default: + throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.RoundingMode}\"."); + } + + if (roundUp) + { + intMant++; + + if (intMant == 1u << F) + { + biasedExp = 1u; + } + + if (intMant == 1u << (F + 1)) + { + biasedExp++; + intMant >>= 1; + } + } + + float result; + + if (biasedExp >= (1u << E) - 1u) + { + result = overflowToInf ? SoftFloat32.FPInfinity(sign) : SoftFloat32.FPMaxNormal(sign); + + SoftFloat.FPProcessException(FPException.Overflow, context); + + error = 1d; + } + else + { + result = BitConverter.Int32BitsToSingle( + (int)((sign ? 1u : 0u) << 31 | (biasedExp & 0xFFu) << 23 | (intMant & 0x007FFFFFu))); + } + + if (error != 0d) + { + SoftFloat.FPProcessException(FPException.Inexact, context); + } + + return result; + } + + private static float FPConvertNaN(ushort valueBits) + { + return BitConverter.Int32BitsToSingle( + (int)(((uint)valueBits & 0x8000u) << 16 | 0x7FC00000u | ((uint)valueBits & 0x01FFu) << 13)); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16_64.cs b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16_64.cs new file mode 100644 index 000000000..5167c1908 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat16_64.cs @@ -0,0 +1,182 @@ +using ARMeilleure.State; +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static class SoftFloat16_64 + { + [UnmanagedCallersOnly] + public static double FPConvert(ushort valueBits) + { + ExecutionContext context = NativeInterface.GetContext(); + + double real = valueBits.FPUnpackCv(out FPType type, out bool sign, context); + + double result; + + if (type is FPType.SNaN or FPType.QNaN) + { + if ((context.Fpcr & FPCR.Dn) != 0) + { + result = SoftFloat64.FPDefaultNaN(); + } + else + { + result = FPConvertNaN(valueBits); + } + + if (type == FPType.SNaN) + { + SoftFloat.FPProcessException(FPException.InvalidOp, context); + } + } + else if (type == FPType.Infinity) + { + result = SoftFloat64.FPInfinity(sign); + } + else if (type == FPType.Zero) + { + result = SoftFloat64.FPZero(sign); + } + else + { + result = FPRoundCv(real, context); + } + + return result; + } + + private static double FPRoundCv(double real, ExecutionContext context) + { + const int MinimumExp = -1022; + + const int E = 11; + const int F = 52; + + bool sign; + double mantissa; + + if (real < 0d) + { + sign = true; + mantissa = -real; + } + else + { + sign = false; + mantissa = real; + } + + int exponent = 0; + + while (mantissa < 1d) + { + mantissa *= 2d; + exponent--; + } + + while (mantissa >= 2d) + { + mantissa /= 2d; + exponent++; + } + + 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); + + if (biasedExp == 0u) + { + 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; + + if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) + { + SoftFloat.FPProcessException(FPException.Underflow, context); + } + + bool overflowToInf; + bool roundUp; + + switch (context.Fpcr.RoundingMode) + { + case FPRoundingMode.ToNearest: + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + overflowToInf = true; + break; + + case FPRoundingMode.TowardsPlusInfinity: + roundUp = (error != 0d && !sign); + overflowToInf = !sign; + break; + + case FPRoundingMode.TowardsMinusInfinity: + roundUp = (error != 0d && sign); + overflowToInf = sign; + break; + + case FPRoundingMode.TowardsZero: + roundUp = false; + overflowToInf = false; + break; + + default: + throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.RoundingMode}\"."); + } + + if (roundUp) + { + intMant++; + + if (intMant == 1ul << F) + { + biasedExp = 1u; + } + + if (intMant == 1ul << (F + 1)) + { + biasedExp++; + intMant >>= 1; + } + } + + double result; + + if (biasedExp >= (1u << E) - 1u) + { + result = overflowToInf ? SoftFloat64.FPInfinity(sign) : SoftFloat64.FPMaxNormal(sign); + + SoftFloat.FPProcessException(FPException.Overflow, context); + + error = 1d; + } + else + { + result = BitConverter.Int64BitsToDouble( + (long)((sign ? 1ul : 0ul) << 63 | (biasedExp & 0x7FFul) << 52 | (intMant & 0x000FFFFFFFFFFFFFul))); + } + + if (error != 0d) + { + SoftFloat.FPProcessException(FPException.Inexact, context); + } + + return result; + } + + private static double FPConvertNaN(ushort valueBits) + { + return BitConverter.Int64BitsToDouble( + (long)(((ulong)valueBits & 0x8000ul) << 48 | 0x7FF8000000000000ul | ((ulong)valueBits & 0x01FFul) << 42)); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFloat/SoftFloat32.cs b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat32.cs new file mode 100644 index 000000000..a7ab054be --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat32.cs @@ -0,0 +1,1421 @@ +using ARMeilleure.State; +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ +static class SoftFloat32 + { + [UnmanagedCallersOnly] + public static float FPAdd(float value1, float value2) + { + return FPAddFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPAddFpscr(float value1, float value2, byte standardFpscr) + { + return FPAddFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static float FPAddFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + 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; + + if (inf1 && inf2 && sign1 == !sign2) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if ((inf1 && !sign1) || (inf2 && !sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 + value2; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static int FPCompare(float value1, float value2, byte signalNaNs) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + int result; + + if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) + { + result = 0b0011; + + if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs == 1) + { + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + } + else + { + if (value1 == value2) + { + result = 0b0110; + } + else if (value1 < value2) + { + result = 0b1000; + } + else + { + result = 0b0010; + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPCompareEQ(float value1, float value2) + { + return FPCompareEQFpscrImpl(value1, value2, false); + } + + private static float FPCompareEQFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + float result; + + if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) + { + result = ZerosOrOnes(false); + + if (type1 == FPType.SNaN || type2 == FPType.SNaN) + { + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + } + else + { + result = ZerosOrOnes(value1 == value2); + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPCompareEQFpscr(float value1, float value2, byte standardFpscr) + { + return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1); + } + + [UnmanagedCallersOnly] + public static float FPCompareGE(float value1, float value2) + { + return FPCompareGEFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPCompareGEFpscr(float value1, float value2, byte standardFpscr) + { + return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static float FPCompareGEFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + float result; + + if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) + { + result = ZerosOrOnes(false); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else + { + result = ZerosOrOnes(value1 >= value2); + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPCompareGT(float value1, float value2) + { + return FPCompareGTFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPCompareGTFpscr(float value1, float value2, byte standardFpscr) + { + return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static float FPCompareGTFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + float result; + + if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) + { + result = ZerosOrOnes(false); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else + { + result = ZerosOrOnes(value1 > value2); + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPCompareLE(float value1, float value2) + { + return FPCompareGEFpscrImpl(value2, value1, false); + } + + [UnmanagedCallersOnly] + public static float FPCompareLT(float value1, float value2) + { + return FPCompareGTFpscrImpl(value2, value1, false); + } + + [UnmanagedCallersOnly] + public static float FPCompareLEFpscr(float value1, float value2, byte standardFpscr) + { + return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1); + } + + [UnmanagedCallersOnly] + public static float FPCompareLTFpscr(float value1, float value2, byte standardFpscr) + { + return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1); + } + + [UnmanagedCallersOnly] + public static float FPDiv(float value1, float value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = 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); + + 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; + + if ((inf1 && inf2) || (zero1 && zero2)) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if (inf1 || zero2) + { + result = FPInfinity(sign1 ^ sign2); + + if (!inf1) + { + SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); + } + } + else if (zero1 || inf2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 / value2; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPMax(float value1, float value2) + { + return FPMaxFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPMaxFpscr(float value1, float value2, byte standardFpscr) + { + return FPMaxFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static float FPMaxFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); + + if (!done) + { + if (value1 > value2) + { + if (type1 == FPType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FPType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value1; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + else + { + if (type2 == FPType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FPType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value2; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPMaxNum(float value1, float value2) + { + return FPMaxNumFpscrImpl(value1, value2, false); + } + + private static float FPMaxNumFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + if (type1 == FPType.QNaN && type2 != FPType.QNaN) + { + value1 = FPInfinity(true); + } + else if (type1 != FPType.QNaN && type2 == FPType.QNaN) + { + value2 = FPInfinity(true); + } + + return FPMaxFpscrImpl(value1, value2, standardFpscr); + } + + [UnmanagedCallersOnly] + public static float FPMaxNumFpscr(float value1, float value2, byte standardFpscr) + { + return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1); + } + + [UnmanagedCallersOnly] + public static float FPMin(float value1, float value2) + { + return FPMinFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPMinFpscr(float value1, float value2, byte standardFpscr) + { + return FPMinFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static float FPMinFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); + + if (!done) + { + if (value1 < value2) + { + if (type1 == FPType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FPType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value1; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + else + { + if (type2 == FPType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FPType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value2; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPMinNum(float value1, float value2) + { + return FPMinNumFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPMinNumFpscr(float value1, float value2, byte standardFpscr) + { + return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static float FPMinNumFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + if (type1 == FPType.QNaN && type2 != FPType.QNaN) + { + value1 = FPInfinity(false); + } + else if (type1 != FPType.QNaN && type2 == FPType.QNaN) + { + value2 = FPInfinity(false); + } + + return FPMinFpscrImpl(value1, value2, standardFpscr); + } + + [UnmanagedCallersOnly] + public static float FPMul(float value1, float value2) + { + return FPMulFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPMulFpscr(float value1, float value2, byte standardFpscr) + { + return FPMulFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static float FPMulFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + 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; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPMulAdd(float valueA, float value1, float value2) + { + return FPMulAddFpscrImpl(valueA, value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPMulAddFpscr(float valueA, float value1, float value2, byte standardFpscr) + { + return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1); + } + + private static float FPMulAddFpscrImpl(float valueA, float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + 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); + + 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); + + if (typeA == FPType.QNaN && ((inf1 && zero2) || (zero1 && inf2))) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + + if (!done) + { + bool infA = typeA == FPType.Infinity; + bool zeroA = typeA == FPType.Zero; + + bool signP = sign1 ^ sign2; + bool infP = inf1 || inf2; + bool zeroP = zero1 || zero2; + + if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if ((infA && !signA) || (infP && !signP)) + { + result = FPInfinity(false); + } + else if ((infA && signA) || (infP && signP)) + { + result = FPInfinity(true); + } + else if (zeroA && zeroP && signA == signP) + { + result = FPZero(signA); + } + else + { + result = MathF.FusedMultiplyAdd(value1, value2, valueA); + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPMulSub(float valueA, float value1, float value2) + { + value1 = value1.FPNeg(); + + return FPMulAddFpscrImpl(valueA, value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPMulSubFpscr(float valueA, float value1, float value2, byte standardFpscr) + { + value1 = value1.FPNeg(); + + return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1); + } + + [UnmanagedCallersOnly] + public static float FPMulX(float value1, float value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = 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); + + 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; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(sign1 ^ sign2); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPNegMulAdd(float valueA, float value1, float value2) + { + valueA = valueA.FPNeg(); + value1 = value1.FPNeg(); + + return FPMulAddFpscrImpl(valueA, value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPNegMulSub(float valueA, float value1, float value2) + { + valueA = valueA.FPNeg(); + + return FPMulAddFpscrImpl(valueA, value1, value2, false); + } + + [UnmanagedCallersOnly] + public static float FPRecipEstimate(float value) + { + return FPRecipEstimateFpscrImpl(value, false); + } + + [UnmanagedCallersOnly] + public static float FPRecipEstimateFpscr(float value, byte standardFpscr) + { + return FPRecipEstimateFpscrImpl(value, standardFpscr == 1); + } + + private static float FPRecipEstimateFpscrImpl(float value, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr); + + float result; + + if (type is FPType.SNaN or FPType.QNaN) + { + result = FPProcessNaN(type, op, context, fpcr); + } + else if (type == FPType.Infinity) + { + result = FPZero(sign); + } + else if (type == FPType.Zero) + { + result = FPInfinity(sign); + + SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); + } + else if (MathF.Abs(value) < MathF.Pow(2f, -128)) + { + bool overflowToInf = fpcr.RoundingMode switch + { + FPRoundingMode.ToNearest => true, + FPRoundingMode.TowardsPlusInfinity => !sign, + FPRoundingMode.TowardsMinusInfinity => sign, + FPRoundingMode.TowardsZero => false, + _ => throw new ArgumentException($"Invalid rounding mode \"{fpcr.RoundingMode}\"."), + }; + result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); + + SoftFloat.FPProcessException(FPException.Overflow, context, fpcr); + SoftFloat.FPProcessException(FPException.Inexact, context, fpcr); + } + else if ((fpcr & FPCR.Fz) != 0 && (MathF.Abs(value) >= MathF.Pow(2f, 126))) + { + result = FPZero(sign); + + context.Fpsr |= FPSR.Ufc; + } + else + { + ulong fraction = (ulong)(op & 0x007FFFFFu) << 29; + uint exp = (op & 0x7F800000u) >> 23; + + if (exp == 0u) + { + if ((fraction & 0x0008000000000000ul) == 0ul) + { + fraction = (fraction & 0x0003FFFFFFFFFFFFul) << 2; + exp -= 1u; + } + else + { + fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; + } + } + + uint scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44); + + uint resultExp = 253u - exp; + + uint estimate = (uint)SoftFloat.RecipEstimateTable[scaled - 256u] + 256u; + + fraction = (ulong)(estimate & 0xFFu) << 44; + + if (resultExp == 0u) + { + fraction = ((fraction & 0x000FFFFFFFFFFFFEul) | 0x0010000000000000ul) >> 1; + } + else if (resultExp + 1u == 0u) + { + fraction = ((fraction & 0x000FFFFFFFFFFFFCul) | 0x0010000000000000ul) >> 2; + resultExp = 0u; + } + + result = BitConverter.Int32BitsToSingle( + (int)((sign ? 1u : 0u) << 31 | (resultExp & 0xFFu) << 23 | (uint)(fraction >> 29) & 0x007FFFFFu)); + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPRecipStep(float value1, float value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.StandardFpcrValue; + + 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; + + float product; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + product = FPZero(false); + } + else + { + product = FPMulFpscrImpl(value1, value2, true); + } + + result = FPSubFpscrImpl(FPTwo(false), product, true); + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPRecipStepFused(float value1, float value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value1 = value1.FPNeg(); + + 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); + + 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; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + result = MathF.FusedMultiplyAdd(value1, value2, 2f); + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPRecpX(float value) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr); + + float result; + + if (type is FPType.SNaN or FPType.QNaN) + { + result = FPProcessNaN(type, op, context, fpcr); + } + else + { + uint notExp = (~op >> 23) & 0xFFu; + uint maxExp = 0xFEu; + + result = BitConverter.Int32BitsToSingle( + (int)((sign ? 1u : 0u) << 31 | (notExp == 0xFFu ? maxExp : notExp) << 23)); + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPRSqrtEstimate(float value) + { + return FPRSqrtEstimateFpscrImpl(value, false); + } + + [UnmanagedCallersOnly] + public static float FPRSqrtEstimateFpscr(float value, byte standardFpscr) + { + return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1); + } + + private static float FPRSqrtEstimateFpscrImpl(float value, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr); + + float result; + + if (type is FPType.SNaN or FPType.QNaN) + { + result = FPProcessNaN(type, op, context, fpcr); + } + else if (type == FPType.Zero) + { + result = FPInfinity(sign); + + SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); + } + else if (sign) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if (type == FPType.Infinity) + { + result = FPZero(false); + } + else + { + ulong fraction = (ulong)(op & 0x007FFFFFu) << 29; + uint exp = (op & 0x7F800000u) >> 23; + + if (exp == 0u) + { + while ((fraction & 0x0008000000000000ul) == 0ul) + { + fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; + exp -= 1u; + } + + fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; + } + + uint scaled; + + if ((exp & 1u) == 0u) + { + scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44); + } + else + { + scaled = (uint)(((fraction & 0x000FE00000000000ul) | 0x0010000000000000ul) >> 45); + } + + uint resultExp = (380u - exp) >> 1; + + uint estimate = (uint)SoftFloat.RecipSqrtEstimateTable[scaled - 128u] + 256u; + + result = BitConverter.Int32BitsToSingle((int)((resultExp & 0xFFu) << 23 | (estimate & 0xFFu) << 15)); + } + + return result; + } + + public static float FPHalvedSub(float value1, float value2, ExecutionContext context, FPCR 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); + + 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; + + if (inf1 && inf2 && sign1 == sign2) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if ((inf1 && !sign1) || (inf2 && sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && !sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == !sign2) + { + result = FPZero(sign1); + } + else + { + result = (value1 - value2) / 2.0f; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPRSqrtStep(float value1, float value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.StandardFpcrValue; + + 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; + + float product; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + product = FPZero(false); + } + else + { + product = FPMulFpscrImpl(value1, value2, true); + } + + result = FPHalvedSub(FPThree(false), product, context, fpcr); + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPRSqrtStepFused(float value1, float value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value1 = value1.FPNeg(); + + 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); + + 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; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPOnePointFive(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + result = MathF.FusedMultiplyAdd(value1, value2, 3f) / 2f; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPSqrt(float value) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value = value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr); + + float result; + + if (type is FPType.SNaN or FPType.QNaN) + { + result = FPProcessNaN(type, op, context, fpcr); + } + else if (type == FPType.Zero) + { + result = FPZero(sign); + } + else if (type == FPType.Infinity && !sign) + { + result = FPInfinity(sign); + } + else if (sign) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else + { + result = MathF.Sqrt(value); + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static float FPSub(float value1, float value2) + { + return FPSubFpscrImpl(value1, value2, false); + } + + private static float FPSubFpscrImpl(float value1, float value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + 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; + + if (inf1 && inf2 && sign1 == sign2) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if ((inf1 && !sign1) || (inf2 && sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && !sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == !sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 - value2; + + if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + public static float FPDefaultNaN() + { + return BitConverter.Int32BitsToSingle(0x7fc00000); + } + + public static float FPInfinity(bool sign) + { + return sign ? float.NegativeInfinity : float.PositiveInfinity; + } + + public static float FPZero(bool sign) + { + return sign ? -0f : +0f; + } + + public static float FPMaxNormal(bool sign) + { + return sign ? float.MinValue : float.MaxValue; + } + + private static float FPTwo(bool sign) + { + return sign ? -2f : +2f; + } + + private static float FPThree(bool sign) + { + return sign ? -3f : +3f; + } + + private static float FPOnePointFive(bool sign) + { + return sign ? -1.5f : +1.5f; + } + + private static float FPNeg(this float value) + { + return -value; + } + + private static float ZerosOrOnes(bool ones) + { + return BitConverter.Int32BitsToSingle(ones ? -1 : 0); + } + + private static float FPUnpack( + this float value, + out FPType type, + out bool sign, + out uint valueBits, + ExecutionContext context, + FPCR fpcr) + { + valueBits = (uint)BitConverter.SingleToInt32Bits(value); + + sign = (~valueBits & 0x80000000u) == 0u; + + if ((valueBits & 0x7F800000u) == 0u) + { + if ((valueBits & 0x007FFFFFu) == 0u || (fpcr & FPCR.Fz) != 0) + { + type = FPType.Zero; + value = FPZero(sign); + + if ((valueBits & 0x007FFFFFu) != 0u) + { + SoftFloat.FPProcessException(FPException.InputDenorm, context, fpcr); + } + } + else + { + type = FPType.Nonzero; + } + } + else if ((~valueBits & 0x7F800000u) == 0u) + { + if ((valueBits & 0x007FFFFFu) == 0u) + { + type = FPType.Infinity; + } + else + { + type = (~valueBits & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN; + value = FPZero(sign); + } + } + else + { + type = FPType.Nonzero; + } + + return value; + } + + private static float FPProcessNaNs( + FPType type1, + FPType type2, + uint op1, + uint op2, + out bool done, + ExecutionContext context, + FPCR fpcr) + { + done = true; + + if (type1 == FPType.SNaN) + { + return FPProcessNaN(type1, op1, context, fpcr); + } + else if (type2 == FPType.SNaN) + { + return FPProcessNaN(type2, op2, context, fpcr); + } + else if (type1 == FPType.QNaN) + { + return FPProcessNaN(type1, op1, context, fpcr); + } + else if (type2 == FPType.QNaN) + { + return FPProcessNaN(type2, op2, context, fpcr); + } + + done = false; + + return FPZero(false); + } + + private static float FPProcessNaNs3( + FPType type1, + FPType type2, + FPType type3, + uint op1, + uint op2, + uint op3, + out bool done, + ExecutionContext context, + FPCR fpcr) + { + done = true; + + if (type1 == FPType.SNaN) + { + return FPProcessNaN(type1, op1, context, fpcr); + } + else if (type2 == FPType.SNaN) + { + return FPProcessNaN(type2, op2, context, fpcr); + } + else if (type3 == FPType.SNaN) + { + return FPProcessNaN(type3, op3, context, fpcr); + } + else if (type1 == FPType.QNaN) + { + return FPProcessNaN(type1, op1, context, fpcr); + } + else if (type2 == FPType.QNaN) + { + return FPProcessNaN(type2, op2, context, fpcr); + } + else if (type3 == FPType.QNaN) + { + return FPProcessNaN(type3, op3, context, fpcr); + } + + done = false; + + return FPZero(false); + } + + private static float FPProcessNaN(FPType type, uint op, ExecutionContext context, FPCR fpcr) + { + if (type == FPType.SNaN) + { + op |= 1u << 22; + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + + if ((fpcr & FPCR.Dn) != 0) + { + return FPDefaultNaN(); + } + + return BitConverter.Int32BitsToSingle((int)op); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFloat/SoftFloat32_16.cs b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat32_16.cs new file mode 100644 index 000000000..67bc5149d --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat32_16.cs @@ -0,0 +1,126 @@ +using ARMeilleure.State; +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static class SoftFloat32_16 + { + [UnmanagedCallersOnly] + public static ushort FPConvert(float value) + { + ExecutionContext context = NativeInterface.GetContext(); + + double real = value.FPUnpackCv(out FPType type, out bool sign, out uint valueBits, context); + + bool altHp = (context.Fpcr & FPCR.Ahp) != 0; + + ushort resultBits; + + if (type is FPType.SNaN or FPType.QNaN) + { + if (altHp) + { + resultBits = SoftFloat16.FPZero(sign); + } + else if ((context.Fpcr & FPCR.Dn) != 0) + { + resultBits = SoftFloat16.FPDefaultNaN(); + } + else + { + resultBits = FPConvertNaN(valueBits); + } + + if (type == FPType.SNaN || altHp) + { + SoftFloat.FPProcessException(FPException.InvalidOp, context); + } + } + else if (type == FPType.Infinity) + { + if (altHp) + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); + + SoftFloat.FPProcessException(FPException.InvalidOp, context); + } + else + { + resultBits = SoftFloat16.FPInfinity(sign); + } + } + else if (type == FPType.Zero) + { + resultBits = SoftFloat16.FPZero(sign); + } + else + { + resultBits = SoftFloat16.FPRoundCv(real, context); + } + + return resultBits; + } + + private static double FPUnpackCv( + this float value, + out FPType type, + out bool sign, + out uint valueBits, + ExecutionContext context) + { + valueBits = (uint)BitConverter.SingleToInt32Bits(value); + + sign = (~valueBits & 0x80000000u) == 0u; + + uint exp32 = (valueBits & 0x7F800000u) >> 23; + uint frac32 = valueBits & 0x007FFFFFu; + + double real; + + if (exp32 == 0u) + { + if (frac32 == 0u || (context.Fpcr & FPCR.Fz) != 0) + { + type = FPType.Zero; + real = 0d; + + if (frac32 != 0u) + { + SoftFloat.FPProcessException(FPException.InputDenorm, context); + } + } + else + { + type = FPType.Nonzero; // Subnormal. + real = Math.Pow(2d, -126) * ((double)frac32 * Math.Pow(2d, -23)); + } + } + else if (exp32 == 0xFFu) + { + if (frac32 == 0u) + { + type = FPType.Infinity; + real = Math.Pow(2d, 1000); + } + else + { + type = (~frac32 & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN; + real = 0d; + } + } + else + { + type = FPType.Nonzero; // Normal. + real = Math.Pow(2d, (int)exp32 - 127) * (1d + (double)frac32 * Math.Pow(2d, -23)); + } + + return sign ? -real : real; + } + + private static ushort FPConvertNaN(uint valueBits) + { + return (ushort)((valueBits & 0x80000000u) >> 16 | 0x7E00u | (valueBits & 0x003FE000u) >> 13); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFloat/SoftFloat64.cs b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat64.cs new file mode 100644 index 000000000..cad132e3a --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat64.cs @@ -0,0 +1,1421 @@ +using ARMeilleure.State; +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static class SoftFloat64 + { + [UnmanagedCallersOnly] + public static double FPAdd(double value1, double value2) + { + return FPAddFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPAddFpscr(double value1, double value2, byte standardFpscr) + { + return FPAddFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPAddFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + 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; + + if (inf1 && inf2 && sign1 == !sign2) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if ((inf1 && !sign1) || (inf2 && !sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 + value2; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static int FPCompare(double value1, double value2, byte signalNaNs) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + int result; + + if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) + { + result = 0b0011; + + if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs == 1) + { + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + } + else + { + if (value1 == value2) + { + result = 0b0110; + } + else if (value1 < value2) + { + result = 0b1000; + } + else + { + result = 0b0010; + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPCompareEQ(double value1, double value2) + { + return FPCompareEQFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPCompareEQFpscr(double value1, double value2, byte standardFpscr) + { + return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPCompareEQFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + double result; + + if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) + { + result = ZerosOrOnes(false); + + if (type1 == FPType.SNaN || type2 == FPType.SNaN) + { + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + } + else + { + result = ZerosOrOnes(value1 == value2); + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPCompareGE(double value1, double value2) + { + return FPCompareGEFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPCompareGEFpscr(double value1, double value2, byte standardFpscr) + { + return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPCompareGEFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + double result; + + if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) + { + result = ZerosOrOnes(false); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else + { + result = ZerosOrOnes(value1 >= value2); + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPCompareGT(double value1, double value2) + { + return FPCompareGTFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPCompareGTFpscr(double value1, double value2, byte standardFpscr) + { + return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPCompareGTFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + double result; + + if (type1 == FPType.SNaN || type1 == FPType.QNaN || type2 == FPType.SNaN || type2 == FPType.QNaN) + { + result = ZerosOrOnes(false); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else + { + result = ZerosOrOnes(value1 > value2); + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPCompareLE(double value1, double value2) + { + return FPCompareGEFpscrImpl(value2, value1, false); + } + + [UnmanagedCallersOnly] + public static double FPCompareLT(double value1, double value2) + { + return FPCompareGTFpscrImpl(value2, value1, false); + } + + [UnmanagedCallersOnly] + public static double FPCompareLEFpscr(double value1, double value2, byte standardFpscr) + { + return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1); + } + + [UnmanagedCallersOnly] + public static double FPCompareLTFpscr(double value1, double value2, byte standardFpscr) + { + return FPCompareGTFpscrImpl(value2, value1, standardFpscr == 1); + } + + [UnmanagedCallersOnly] + public static double FPDiv(double value1, double value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = 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); + + 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; + + if ((inf1 && inf2) || (zero1 && zero2)) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if (inf1 || zero2) + { + result = FPInfinity(sign1 ^ sign2); + + if (!inf1) + { + SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); + } + } + else if (zero1 || inf2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 / value2; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPMax(double value1, double value2) + { + return FPMaxFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPMaxFpscr(double value1, double value2, byte standardFpscr) + { + return FPMaxFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPMaxFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); + + if (!done) + { + if (value1 > value2) + { + if (type1 == FPType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FPType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value1; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + else + { + if (type2 == FPType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FPType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value2; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPMaxNum(double value1, double value2) + { + return FPMaxNumFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPMaxNumFpscr(double value1, double value2, byte standardFpscr) + { + return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPMaxNumFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + if (type1 == FPType.QNaN && type2 != FPType.QNaN) + { + value1 = FPInfinity(true); + } + else if (type1 != FPType.QNaN && type2 == FPType.QNaN) + { + value2 = FPInfinity(true); + } + + return FPMaxFpscrImpl(value1, value2, standardFpscr); + } + + [UnmanagedCallersOnly] + public static double FPMin(double value1, double value2) + { + return FPMinFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPMinFpscr(double value1, double value2, byte standardFpscr) + { + return FPMinFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPMinFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); + + if (!done) + { + if (value1 < value2) + { + if (type1 == FPType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FPType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value1; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + else + { + if (type2 == FPType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FPType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value2; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPMinNum(double value1, double value2) + { + return FPMinNumFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPMinNumFpscr(double value1, double value2, byte standardFpscr) + { + return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPMinNumFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); + + if (type1 == FPType.QNaN && type2 != FPType.QNaN) + { + value1 = FPInfinity(false); + } + else if (type1 != FPType.QNaN && type2 == FPType.QNaN) + { + value2 = FPInfinity(false); + } + + return FPMinFpscrImpl(value1, value2, standardFpscr); + } + + [UnmanagedCallersOnly] + public static double FPMul(double value1, double value2) + { + return FPMulFpscrImpl(value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPMulFpscr(double value1, double value2, byte standardFpscr) + { + return FPMulFpscrImpl(value1, value2, standardFpscr == 1); + } + + private static double FPMulFpscrImpl(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + 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; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPMulAdd(double valueA, double value1, double value2) + { + return FPMulAddFpscrImpl(valueA, value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPMulAddFpscr(double valueA, double value1, double value2, byte standardFpscr) + { + return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1); + } + + private static double FPMulAddFpscrImpl(double valueA, double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + 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); + + 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); + + if (typeA == FPType.QNaN && ((inf1 && zero2) || (zero1 && inf2))) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + + if (!done) + { + bool infA = typeA == FPType.Infinity; + bool zeroA = typeA == FPType.Zero; + + bool signP = sign1 ^ sign2; + bool infP = inf1 || inf2; + bool zeroP = zero1 || zero2; + + if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if ((infA && !signA) || (infP && !signP)) + { + result = FPInfinity(false); + } + else if ((infA && signA) || (infP && signP)) + { + result = FPInfinity(true); + } + else if (zeroA && zeroP && signA == signP) + { + result = FPZero(signA); + } + else + { + result = Math.FusedMultiplyAdd(value1, value2, valueA); + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPMulSub(double valueA, double value1, double value2) + { + value1 = value1.FPNeg(); + + return FPMulAddFpscrImpl(valueA, value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPMulSubFpscr(double valueA, double value1, double value2, byte standardFpscr) + { + value1 = value1.FPNeg(); + + return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1); + } + + [UnmanagedCallersOnly] + public static double FPMulX(double value1, double value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = 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); + + 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; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(sign1 ^ sign2); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPNegMulAdd(double valueA, double value1, double value2) + { + valueA = valueA.FPNeg(); + value1 = value1.FPNeg(); + + return FPMulAddFpscrImpl(valueA, value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPNegMulSub(double valueA, double value1, double value2) + { + valueA = valueA.FPNeg(); + + return FPMulAddFpscrImpl(valueA, value1, value2, false); + } + + [UnmanagedCallersOnly] + public static double FPRecipEstimate(double value) + { + return FPRecipEstimateFpscrImpl(value, false); + } + + [UnmanagedCallersOnly] + public static double FPRecipEstimateFpscr(double value, byte standardFpscr) + { + return FPRecipEstimateFpscrImpl(value, standardFpscr == 1); + } + + private static double FPRecipEstimateFpscrImpl(double value, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr); + + double result; + + if (type is FPType.SNaN or FPType.QNaN) + { + result = FPProcessNaN(type, op, context, fpcr); + } + else if (type == FPType.Infinity) + { + result = FPZero(sign); + } + else if (type == FPType.Zero) + { + result = FPInfinity(sign); + + SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); + } + else if (Math.Abs(value) < Math.Pow(2d, -1024)) + { + bool overflowToInf = fpcr.RoundingMode switch + { + FPRoundingMode.ToNearest => true, + FPRoundingMode.TowardsPlusInfinity => !sign, + FPRoundingMode.TowardsMinusInfinity => sign, + FPRoundingMode.TowardsZero => false, + _ => throw new ArgumentException($"Invalid rounding mode \"{fpcr.RoundingMode}\"."), + }; + result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); + + SoftFloat.FPProcessException(FPException.Overflow, context, fpcr); + SoftFloat.FPProcessException(FPException.Inexact, context, fpcr); + } + else if ((fpcr & FPCR.Fz) != 0 && (Math.Abs(value) >= Math.Pow(2d, 1022))) + { + result = FPZero(sign); + + context.Fpsr |= FPSR.Ufc; + } + else + { + ulong fraction = op & 0x000FFFFFFFFFFFFFul; + uint exp = (uint)((op & 0x7FF0000000000000ul) >> 52); + + if (exp == 0u) + { + if ((fraction & 0x0008000000000000ul) == 0ul) + { + fraction = (fraction & 0x0003FFFFFFFFFFFFul) << 2; + exp -= 1u; + } + else + { + fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; + } + } + + uint scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44); + + uint resultExp = 2045u - exp; + + uint estimate = (uint)SoftFloat.RecipEstimateTable[scaled - 256u] + 256u; + + fraction = (ulong)(estimate & 0xFFu) << 44; + + if (resultExp == 0u) + { + fraction = ((fraction & 0x000FFFFFFFFFFFFEul) | 0x0010000000000000ul) >> 1; + } + else if (resultExp + 1u == 0u) + { + fraction = ((fraction & 0x000FFFFFFFFFFFFCul) | 0x0010000000000000ul) >> 2; + resultExp = 0u; + } + + result = BitConverter.Int64BitsToDouble( + (long)((sign ? 1ul : 0ul) << 63 | (resultExp & 0x7FFul) << 52 | (fraction & 0x000FFFFFFFFFFFFFul))); + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPRecipStep(double value1, double value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.StandardFpcrValue; + + 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; + + double product; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + product = FPZero(false); + } + else + { + product = FPMulFpscrImpl(value1, value2, true); + } + + result = FPSubFpscr(FPTwo(false), product, true); + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPRecipStepFused(double value1, double value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value1 = value1.FPNeg(); + + 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); + + 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; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + result = Math.FusedMultiplyAdd(value1, value2, 2d); + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPRecpX(double value) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr); + + double result; + + if (type is FPType.SNaN or FPType.QNaN) + { + result = FPProcessNaN(type, op, context, fpcr); + } + else + { + ulong notExp = (~op >> 52) & 0x7FFul; + ulong maxExp = 0x7FEul; + + result = BitConverter.Int64BitsToDouble( + (long)((sign ? 1ul : 0ul) << 63 | (notExp == 0x7FFul ? maxExp : notExp) << 52)); + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPRSqrtEstimate(double value) + { + return FPRSqrtEstimateFpscrImpl(value, false); + } + + [UnmanagedCallersOnly] + public static double FPRSqrtEstimateFpscr(double value, byte standardFpscr) + { + return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1); + } + + private static double FPRSqrtEstimateFpscrImpl(double value, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; + + value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr); + + double result; + + if (type is FPType.SNaN or FPType.QNaN) + { + result = FPProcessNaN(type, op, context, fpcr); + } + else if (type == FPType.Zero) + { + result = FPInfinity(sign); + + SoftFloat.FPProcessException(FPException.DivideByZero, context, fpcr); + } + else if (sign) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if (type == FPType.Infinity) + { + result = FPZero(false); + } + else + { + ulong fraction = op & 0x000FFFFFFFFFFFFFul; + uint exp = (uint)((op & 0x7FF0000000000000ul) >> 52); + + if (exp == 0u) + { + while ((fraction & 0x0008000000000000ul) == 0ul) + { + fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; + exp -= 1u; + } + + fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1; + } + + uint scaled; + + if ((exp & 1u) == 0u) + { + scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44); + } + else + { + scaled = (uint)(((fraction & 0x000FE00000000000ul) | 0x0010000000000000ul) >> 45); + } + + uint resultExp = (3068u - exp) >> 1; + + uint estimate = (uint)SoftFloat.RecipSqrtEstimateTable[scaled - 128u] + 256u; + + result = BitConverter.Int64BitsToDouble((long)((resultExp & 0x7FFul) << 52 | (estimate & 0xFFul) << 44)); + } + + return result; + } + + public static double FPHalvedSub(double value1, double value2, ExecutionContext context, FPCR 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); + + 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; + + if (inf1 && inf2 && sign1 == sign2) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if ((inf1 && !sign1) || (inf2 && sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && !sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == !sign2) + { + result = FPZero(sign1); + } + else + { + result = (value1 - value2) / 2.0; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPRSqrtStep(double value1, double value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.StandardFpcrValue; + + 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; + + double product; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + product = FPZero(false); + } + else + { + product = FPMulFpscrImpl(value1, value2, true); + } + + result = FPHalvedSub(FPThree(false), product, context, fpcr); + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPRSqrtStepFused(double value1, double value2) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value1 = value1.FPNeg(); + + 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); + + 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; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPOnePointFive(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + result = Math.FusedMultiplyAdd(value1, value2, 3d) / 2d; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPSqrt(double value) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = context.Fpcr; + + value = value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr); + + double result; + + if (type is FPType.SNaN or FPType.QNaN) + { + result = FPProcessNaN(type, op, context, fpcr); + } + else if (type == FPType.Zero) + { + result = FPZero(sign); + } + else if (type == FPType.Infinity && !sign) + { + result = FPInfinity(sign); + } + else if (sign) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else + { + result = Math.Sqrt(value); + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + + return result; + } + + [UnmanagedCallersOnly] + public static double FPSub(double value1, double value2) + { + return FPSubFpscr(value1, value2, false); + } + + public static double FPSubFpscr(double value1, double value2, bool standardFpscr) + { + ExecutionContext context = NativeInterface.GetContext(); + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : 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); + + 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; + + if (inf1 && inf2 && sign1 == sign2) + { + result = FPDefaultNaN(); + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + else if ((inf1 && !sign1) || (inf2 && sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && !sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == !sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 - value2; + + if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result)) + { + context.Fpsr |= FPSR.Ufc; + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + public static double FPDefaultNaN() + { + return BitConverter.Int64BitsToDouble(0x7ff8000000000000); + } + + public static double FPInfinity(bool sign) + { + return sign ? double.NegativeInfinity : double.PositiveInfinity; + } + + public static double FPZero(bool sign) + { + return sign ? -0d : +0d; + } + + public static double FPMaxNormal(bool sign) + { + return sign ? double.MinValue : double.MaxValue; + } + + private static double FPTwo(bool sign) + { + return sign ? -2d : +2d; + } + + private static double FPThree(bool sign) + { + return sign ? -3d : +3d; + } + + private static double FPOnePointFive(bool sign) + { + return sign ? -1.5d : +1.5d; + } + + private static double FPNeg(this double value) + { + return -value; + } + + private static double ZerosOrOnes(bool ones) + { + return BitConverter.Int64BitsToDouble(ones ? -1L : 0L); + } + + private static double FPUnpack( + this double value, + out FPType type, + out bool sign, + out ulong valueBits, + ExecutionContext context, + FPCR fpcr) + { + valueBits = (ulong)BitConverter.DoubleToInt64Bits(value); + + sign = (~valueBits & 0x8000000000000000ul) == 0ul; + + if ((valueBits & 0x7FF0000000000000ul) == 0ul) + { + if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul || (fpcr & FPCR.Fz) != 0) + { + type = FPType.Zero; + value = FPZero(sign); + + if ((valueBits & 0x000FFFFFFFFFFFFFul) != 0ul) + { + SoftFloat.FPProcessException(FPException.InputDenorm, context, fpcr); + } + } + else + { + type = FPType.Nonzero; + } + } + else if ((~valueBits & 0x7FF0000000000000ul) == 0ul) + { + if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul) + { + type = FPType.Infinity; + } + else + { + type = (~valueBits & 0x0008000000000000ul) == 0ul ? FPType.QNaN : FPType.SNaN; + value = FPZero(sign); + } + } + else + { + type = FPType.Nonzero; + } + + return value; + } + + private static double FPProcessNaNs( + FPType type1, + FPType type2, + ulong op1, + ulong op2, + out bool done, + ExecutionContext context, + FPCR fpcr) + { + done = true; + + if (type1 == FPType.SNaN) + { + return FPProcessNaN(type1, op1, context, fpcr); + } + else if (type2 == FPType.SNaN) + { + return FPProcessNaN(type2, op2, context, fpcr); + } + else if (type1 == FPType.QNaN) + { + return FPProcessNaN(type1, op1, context, fpcr); + } + else if (type2 == FPType.QNaN) + { + return FPProcessNaN(type2, op2, context, fpcr); + } + + done = false; + + return FPZero(false); + } + + private static double FPProcessNaNs3( + FPType type1, + FPType type2, + FPType type3, + ulong op1, + ulong op2, + ulong op3, + out bool done, + ExecutionContext context, + FPCR fpcr) + { + done = true; + + if (type1 == FPType.SNaN) + { + return FPProcessNaN(type1, op1, context, fpcr); + } + else if (type2 == FPType.SNaN) + { + return FPProcessNaN(type2, op2, context, fpcr); + } + else if (type3 == FPType.SNaN) + { + return FPProcessNaN(type3, op3, context, fpcr); + } + else if (type1 == FPType.QNaN) + { + return FPProcessNaN(type1, op1, context, fpcr); + } + else if (type2 == FPType.QNaN) + { + return FPProcessNaN(type2, op2, context, fpcr); + } + else if (type3 == FPType.QNaN) + { + return FPProcessNaN(type3, op3, context, fpcr); + } + + done = false; + + return FPZero(false); + } + + private static double FPProcessNaN(FPType type, ulong op, ExecutionContext context, FPCR fpcr) + { + if (type == FPType.SNaN) + { + op |= 1ul << 51; + + SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr); + } + + if ((fpcr & FPCR.Dn) != 0) + { + return FPDefaultNaN(); + } + + return BitConverter.Int64BitsToDouble((long)op); + } + } +} diff --git a/src/ARMeilleure/Instructions/SoftFloat/SoftFloat64_16.cs b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat64_16.cs new file mode 100644 index 000000000..71bc84da5 --- /dev/null +++ b/src/ARMeilleure/Instructions/SoftFloat/SoftFloat64_16.cs @@ -0,0 +1,127 @@ +using ARMeilleure.State; +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Instructions +{ + static class SoftFloat64_16 + { + [UnmanagedCallersOnly] + public static ushort FPConvert(double value) + { + ExecutionContext context = NativeInterface.GetContext(); + + double real = value.FPUnpackCv(out FPType type, out bool sign, out ulong valueBits, context); + + bool altHp = (context.Fpcr & FPCR.Ahp) != 0; + + ushort resultBits; + + if (type is FPType.SNaN or FPType.QNaN) + { + if (altHp) + { + resultBits = SoftFloat16.FPZero(sign); + } + else if ((context.Fpcr & FPCR.Dn) != 0) + { + resultBits = SoftFloat16.FPDefaultNaN(); + } + else + { + resultBits = FPConvertNaN(valueBits); + } + + if (type == FPType.SNaN || altHp) + { + SoftFloat.FPProcessException(FPException.InvalidOp, context); + } + } + else if (type == FPType.Infinity) + { + if (altHp) + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); + + SoftFloat.FPProcessException(FPException.InvalidOp, context); + } + else + { + resultBits = SoftFloat16.FPInfinity(sign); + } + } + else if (type == FPType.Zero) + { + resultBits = SoftFloat16.FPZero(sign); + } + else + { + resultBits = SoftFloat16.FPRoundCv(real, context); + } + + return resultBits; + } + + private static double FPUnpackCv( + this double value, + out FPType type, + out bool sign, + out ulong valueBits, + ExecutionContext context) + { + valueBits = (ulong)BitConverter.DoubleToInt64Bits(value); + + sign = (~valueBits & 0x8000000000000000ul) == 0u; + + ulong exp64 = (valueBits & 0x7FF0000000000000ul) >> 52; + ulong frac64 = valueBits & 0x000FFFFFFFFFFFFFul; + + double real; + + if (exp64 == 0u) + { + if (frac64 == 0u || (context.Fpcr & FPCR.Fz) != 0) + { + type = FPType.Zero; + real = 0d; + + if (frac64 != 0u) + { + SoftFloat.FPProcessException(FPException.InputDenorm, context); + } + } + else + { + type = FPType.Nonzero; // Subnormal. + real = Math.Pow(2d, -1022) * ((double)frac64 * Math.Pow(2d, -52)); + } + } + else if (exp64 == 0x7FFul) + { + if (frac64 == 0u) + { + type = FPType.Infinity; + real = Math.Pow(2d, 1000000); + } + else + { + type = (~frac64 & 0x0008000000000000ul) == 0u ? FPType.QNaN : FPType.SNaN; + real = 0d; + } + } + else + { + type = FPType.Nonzero; // Normal. + real = Math.Pow(2d, (int)exp64 - 1023) * (1d + (double)frac64 * Math.Pow(2d, -52)); + } + + return sign ? -real : real; + } + + private static ushort FPConvertNaN(ulong valueBits) + { + return (ushort)((valueBits & 0x8000000000000000ul) >> 48 | 0x7E00u | + (valueBits & 0x0007FC0000000000ul) >> 42); + } + } +} diff --git a/src/ARMeilleure/IntermediateRepresentation/Comparison.cs b/src/ARMeilleure/IntermediateRepresentation/Comparison.cs index 3d6a9d818..c9d3b5c76 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Comparison.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Comparison.cs @@ -16,9 +16,9 @@ namespace ARMeilleure.IntermediateRepresentation static class ComparisonExtensions { - public static Comparison Invert(this Comparison comp) + extension(Comparison comparison) { - return (Comparison)((int)comp ^ 1); + public Comparison Inverse => (Comparison)((int)comparison ^ 1); } } } diff --git a/src/ARMeilleure/IntermediateRepresentation/OperandType.cs b/src/ARMeilleure/IntermediateRepresentation/OperandType.cs index fec22eed6..027be8cac 100644 --- a/src/ARMeilleure/IntermediateRepresentation/OperandType.cs +++ b/src/ARMeilleure/IntermediateRepresentation/OperandType.cs @@ -14,48 +14,38 @@ namespace ARMeilleure.IntermediateRepresentation static class OperandTypeExtensions { - public static bool IsInteger(this OperandType type) + extension(OperandType type) { - return type is OperandType.I32 or - OperandType.I64; - } - - public static RegisterType ToRegisterType(this OperandType type) - { - return type switch + public bool IsInteger => type is OperandType.I32 or OperandType.I64; + + public RegisterType Register => type switch { 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}\"."), + _ => throw new InvalidOperationException($"Invalid operand type \"{type}\".") }; - } - - public static int GetSizeInBytes(this OperandType type) - { - return type switch + + public int ByteSize => type switch { OperandType.FP32 => 4, OperandType.FP64 => 8, OperandType.I32 => 4, OperandType.I64 => 8, OperandType.V128 => 16, - _ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."), + _ => throw new InvalidOperationException($"Invalid operand type \"{type}\".") }; - } - - public static int GetSizeInBytesLog2(this OperandType type) - { - return type switch + + public int ByteSizeLog2 => type switch { OperandType.FP32 => 2, OperandType.FP64 => 3, OperandType.I32 => 2, OperandType.I64 => 3, OperandType.V128 => 4, - _ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."), + _ => throw new InvalidOperationException($"Invalid operand type \"{type}\".") }; } } diff --git a/src/ARMeilleure/Memory/MemoryManagerType.cs b/src/ARMeilleure/Memory/MemoryManagerType.cs index cad7c3558..ce417ee7e 100644 --- a/src/ARMeilleure/Memory/MemoryManagerType.cs +++ b/src/ARMeilleure/Memory/MemoryManagerType.cs @@ -45,19 +45,12 @@ namespace ARMeilleure.Memory public static class MemoryManagerTypeExtensions { - public static bool IsHostMapped(this MemoryManagerType type) + extension(MemoryManagerType type) { - return type is MemoryManagerType.HostMapped or MemoryManagerType.HostMappedUnsafe; - } - - public static bool IsHostTracked(this MemoryManagerType type) - { - return type is MemoryManagerType.HostTracked or MemoryManagerType.HostTrackedUnsafe; - } - - public static bool IsHostMappedOrTracked(this MemoryManagerType type) - { - return type.IsHostMapped() || type.IsHostTracked(); + public bool IsHostMapped => type is MemoryManagerType.HostMapped or MemoryManagerType.HostMappedUnsafe; + public bool IsHostTracked => type is MemoryManagerType.HostTracked or MemoryManagerType.HostTrackedUnsafe; + + public bool IsHostMappedOrTracked => type.IsHostMapped || type.IsHostTracked; } } } diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index 073b7ffe2..0ebb705a4 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -412,7 +412,7 @@ namespace ARMeilleure.Translation { context.SyncQcFlag(); - if (block.Branch != null && !block.Branch.Exit && block.Branch.Address <= block.Address) + if (block.Branch is { Exit: false } && block.Branch.Address <= block.Address) { EmitSynchronization(context); } @@ -429,14 +429,14 @@ namespace ARMeilleure.Translation { lblPredicateSkip = Label(); - InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, context.CurrentIfThenBlockCond.Invert()); + InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, context.CurrentIfThenBlockCond.Inverse); } - if (opCode is OpCode32 op && op.Cond < Condition.Al) + if (opCode is OpCode32 { Cond: < Condition.Al } op) { lblPredicateSkip = Label(); - InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, op.Cond.Invert()); + InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, op.Cond.Inverse); } if (opCode.Instruction.Emitter != null) diff --git a/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs index a9acabec9..0410314c4 100644 --- a/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs @@ -58,16 +58,16 @@ namespace Ryujinx.Audio.Backends.CompatLayer switch (realSampleFormat) { case SampleFormat.PcmInt8: - PcmHelper.ConvertSampleToPcm8(MemoryMarshal.Cast(convertedSamples), samples); + PcmHelper.ConvertSampleToPcm8(MemoryMarshal.Cast(new Span(convertedSamples)), samples); break; case SampleFormat.PcmInt24: PcmHelper.ConvertSampleToPcm24(convertedSamples, samples); break; case SampleFormat.PcmInt32: - PcmHelper.ConvertSampleToPcm32(MemoryMarshal.Cast(convertedSamples), samples); + PcmHelper.ConvertSampleToPcm32(MemoryMarshal.Cast(new Span(convertedSamples)), samples); break; case SampleFormat.PcmFloat: - PcmHelper.ConvertSampleToPcmFloat(MemoryMarshal.Cast(convertedSamples), samples); + PcmHelper.ConvertSampleToPcmFloat(MemoryMarshal.Cast(new Span(convertedSamples)), samples); break; default: throw new NotImplementedException($"Sample format conversion from {_userSampleFormat} to {realSampleFormat} not implemented."); diff --git a/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs b/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs index 1369f953a..32457d09a 100644 --- a/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs +++ b/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Audio.Integration public void AppendBuffer(ReadOnlySpan data, uint channelCount) { - data.CopyTo(MemoryMarshal.Cast(_buffer)); + data.CopyTo(MemoryMarshal.Cast(new Span(_buffer))); _session.QueueBuffer(new AudioBuffer { diff --git a/src/Ryujinx.Cpu/Jit/JitCpuContext.cs b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs index a29def8e8..9b2bf7015 100644 --- a/src/Ryujinx.Cpu/Jit/JitCpuContext.cs +++ b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Cpu.Jit _functionTable = AddressTable.CreateForArm(for64Bit, memory.Type); _translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable); - if (memory.Type.IsHostMappedOrTracked()) + if (memory.Type.IsHostMappedOrTracked) { NativeSignalHandler.InitializeSignalHandler(); } diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/ScopedRegister.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/ScopedRegister.cs index 18b1416ea..e4ebf1309 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/ScopedRegister.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/ScopedRegister.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32 return; } - if (_operand.Type.IsInteger()) + if (_operand.Type.IsInteger) { _registerAllocator.FreeTempGprRegister(_operand.AsInt32()); } diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs index 643d1e20d..ff11cbd41 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs @@ -381,7 +381,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 if (currentCond != ArmCondition.Al) { instructionPointer = context.CodeWriter.InstructionPointer; - context.Arm64Assembler.B(currentCond.Invert(), 0); + context.Arm64Assembler.B(currentCond.Inverse, 0); } } } diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs index 8190bd7ea..943bc6897 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs @@ -104,7 +104,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 if (invert) { - conditions[i++] = ((ArmCondition)firstCond).Invert(); + conditions[i++] = ((ArmCondition)firstCond).Inverse; } else { diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMemory.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMemory.cs index d8caee6e7..761335c47 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMemory.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMemory.cs @@ -1129,7 +1129,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 // We don't need to mask the address for the safe mode, since it is already naturally limited to 32-bit // and can never reach out of the guest address space. - if (mmType.IsHostTracked()) + if (mmType.IsHostTracked) { int tempRegister = regAlloc.AllocateTempGprRegister(); @@ -1141,7 +1141,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 regAlloc.FreeTempGprRegister(tempRegister); } - else if (mmType.IsHostMapped()) + else if (mmType.IsHostMapped) { asm.Add(destination64, basePointer, guestAddress); } diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Block.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Block.cs index 405126357..43ad1b3b6 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/Block.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Block.cs @@ -132,7 +132,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64 InstName lastInstructionName = Instructions[^1].Name; - return lastInstructionName.IsCall() || lastInstructionName.IsException(); + return lastInstructionName.IsCall || lastInstructionName.IsException; } } } diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/InstName.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/InstName.cs index af3b872a5..8a937134f 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/InstName.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/InstName.cs @@ -1042,126 +1042,39 @@ namespace Ryujinx.Cpu.LightningJit.Arm64 static class InstNameExtensions { - public static bool IsCall(this InstName name) + extension(InstName name) { - return name is InstName.Bl or InstName.Blr; - } + public bool IsCall => name is InstName.Bl or InstName.Blr; - public static bool IsControlFlowOrException(this InstName name) - { - switch (name) + public bool IsControlFlowOrException => name is + InstName.BUncond or InstName.BCond or InstName.Bl or InstName.Blr or InstName.Br or InstName.Brk + or InstName.Cbnz or InstName.Cbz or InstName.Ret or InstName.Tbnz or InstName.Tbz or InstName.Svc + or InstName.UdfPermUndef; + + public bool IsException => name is InstName.Brk or InstName.Svc or InstName.UdfPermUndef; + + public bool IsSystem => name switch { - case InstName.BUncond: - case InstName.BCond: - case InstName.Bl: - case InstName.Blr: - case InstName.Br: - case InstName.Brk: - case InstName.Cbnz: - case InstName.Cbz: - case InstName.Ret: - case InstName.Tbnz: - case InstName.Tbz: - case InstName.Svc: - case InstName.UdfPermUndef: - return true; - } + InstName.Mrs or InstName.MsrImm or InstName.MsrReg => true, + _ => name.IsException + }; - return false; - } + public bool IsSystemOrCall => name.IsCall || name is + InstName.Svc or InstName.Mrs or InstName.MsrImm or InstName.MsrReg + or InstName.Sysl; - public static bool IsException(this InstName name) - { - switch (name) - { - case InstName.Brk: - case InstName.Svc: - case InstName.UdfPermUndef: - return true; - } + public bool IsPrivileged => name is + InstName.Dcps1 or InstName.Dcps2 or InstName.Dcps3 or InstName.Drps or InstName.Eret or InstName.Ereta + or InstName.Hvc or InstName.MsrImm or InstName.Smc; - return false; - } + public bool IsPartialRegisterUpdateMemory => name is + InstName.Ld1AdvsimdSnglAsNoPostIndex or InstName.Ld1AdvsimdSnglAsPostIndex + or InstName.Ld2AdvsimdSnglAsNoPostIndex or InstName.Ld2AdvsimdSnglAsPostIndex + or InstName.Ld3AdvsimdSnglAsNoPostIndex or InstName.Ld3AdvsimdSnglAsPostIndex + or InstName.Ld4AdvsimdSnglAsNoPostIndex or InstName.Ld4AdvsimdSnglAsPostIndex; - public static bool IsSystem(this InstName name) - { - switch (name) - { - case InstName.Mrs: - case InstName.MsrImm: - case InstName.MsrReg: - return true; - } - - return name.IsException(); - } - - public static bool IsSystemOrCall(this InstName name) - { - switch (name) - { - case InstName.Bl: - case InstName.Blr: - case InstName.Svc: - case InstName.Mrs: - case InstName.MsrImm: - case InstName.MsrReg: - case InstName.Sysl: - return true; - } - - return false; - } - - public static bool IsPrivileged(this InstName name) - { - switch (name) - { - case InstName.Dcps1: - case InstName.Dcps2: - case InstName.Dcps3: - case InstName.Drps: - case InstName.Eret: - case InstName.Ereta: - case InstName.Hvc: - case InstName.MsrImm: - case InstName.Smc: - return true; - } - - return false; - } - - public static bool IsPartialRegisterUpdateMemory(this InstName name) - { - switch (name) - { - case InstName.Ld1AdvsimdSnglAsNoPostIndex: - case InstName.Ld1AdvsimdSnglAsPostIndex: - case InstName.Ld2AdvsimdSnglAsNoPostIndex: - case InstName.Ld2AdvsimdSnglAsPostIndex: - case InstName.Ld3AdvsimdSnglAsNoPostIndex: - case InstName.Ld3AdvsimdSnglAsPostIndex: - case InstName.Ld4AdvsimdSnglAsNoPostIndex: - case InstName.Ld4AdvsimdSnglAsPostIndex: - return true; - } - - return false; - } - - public static bool IsPrefetchMemory(this InstName name) - { - switch (name) - { - case InstName.PrfmImm: - case InstName.PrfmLit: - case InstName.PrfmReg: - case InstName.Prfum: - return true; - } - - return false; + public bool IsPrefetchMemory => name is + InstName.PrfmImm or InstName.PrfmLit or InstName.PrfmReg or InstName.Prfum; } } } diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/RegisterAllocator.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/RegisterAllocator.cs index 1c6eab0de..425575df6 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/RegisterAllocator.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/RegisterAllocator.cs @@ -150,7 +150,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64 public static int CalculateMaxTemps(MemoryManagerType mmType) { - return mmType.IsHostMapped() ? 1 : 2; + return mmType.IsHostMapped ? 1 : 2; } public static int CalculateMaxTempsInclFixed(MemoryManagerType mmType) diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/RegisterUtils.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/RegisterUtils.cs index 191e03e7b..c0ee93518 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/RegisterUtils.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/RegisterUtils.cs @@ -247,7 +247,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64 } } - if (!flags.HasFlag(InstFlags.ReadRt) || name.IsPartialRegisterUpdateMemory()) + if (!flags.HasFlag(InstFlags.ReadRt) || name.IsPartialRegisterUpdateMemory) { if (flags.HasFlag(InstFlags.Rt)) { @@ -281,7 +281,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64 gprMask |= MaskFromIndex(ExtractRd(flags, encoding)); } - if (!flags.HasFlag(InstFlags.ReadRt) || name.IsPartialRegisterUpdateMemory()) + if (!flags.HasFlag(InstFlags.ReadRt) || name.IsPartialRegisterUpdateMemory) { if (flags.HasFlag(InstFlags.Rt)) { diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Compiler.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Compiler.cs index e45d74f9b..ac389f4ce 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Compiler.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Compiler.cs @@ -364,7 +364,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 { InstEmitMemory.RewriteSysInstruction(memoryManager.AddressSpaceBits, memoryManager.Type, writer, regAlloc, encoding); } - else if (instInfo.Name.IsSystem()) + else if (instInfo.Name.IsSystem) { bool needsContextStoreLoad = InstEmitSystem.NeedsContextStoreLoad(instInfo.Name); @@ -405,7 +405,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 lastInstructionEncoding = RegisterUtils.RemapRegisters(regAlloc, lastInstructionFlags, lastInstructionEncoding); - if (lastInstructionName.IsCall()) + if (lastInstructionName.IsCall) { context.StoreToContextBeforeCall(blockIndex, pc + 4UL); diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Decoder.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Decoder.cs index ad221c7aa..931c7381e 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Decoder.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Decoder.cs @@ -257,7 +257,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 (name, flags, AddressForm addressForm) = InstTable.GetInstNameAndFlags(encoding, cpuPreset.Version, cpuPreset.Features); - if (name.IsPrivileged() || (name == InstName.Sys && IsPrivilegedSys(encoding))) + if (name.IsPrivileged || (name == InstName.Sys && IsPrivilegedSys(encoding))) { name = InstName.UdfPermUndef; flags = InstFlags.None; @@ -267,7 +267,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 (uint instGprReadMask, uint instFpSimdReadMask) = RegisterUtils.PopulateReadMasks(name, flags, encoding); (uint instGprWriteMask, uint instFpSimdWriteMask) = RegisterUtils.PopulateWriteMasks(name, flags, encoding); - if (name.IsCall()) + if (name.IsCall) { instGprWriteMask |= 1u << RegisterUtils.LrIndex; } @@ -310,12 +310,12 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 fpSimdUseMask |= instFpSimdReadMask | instFpSimdWriteMask; pStateUseMask |= instPStateReadMask | instPStateWriteMask; - if (name.IsSystemOrCall() && !hasHostCall) + if (name.IsSystemOrCall && !hasHostCall) { - hasHostCall = name.IsCall() || InstEmitSystem.NeedsCall(encoding); + hasHostCall = name.IsCall || InstEmitSystem.NeedsCall(encoding); } - isControlFlow = name.IsControlFlowOrException(); + isControlFlow = name.IsControlFlowOrException; RegisterUse registerUse = new( instGprReadMask, @@ -339,7 +339,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 useMask = new(gprUseMask, fpSimdUseMask, pStateUseMask); - return new(startAddress, address, insts, !isTruncated && !name.IsException(), isTruncated, isLoopEnd); + return new(startAddress, address, insts, !isTruncated && !name.IsException, isTruncated, isLoopEnd); } private static bool IsPrivilegedSys(uint encoding) diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitMemory.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitMemory.cs index 790a7de95..e9d6c5c86 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitMemory.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitMemory.cs @@ -55,7 +55,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 ulong pc, uint encoding) { - if (name.IsPrefetchMemory() && mmType == MemoryManagerType.HostTrackedUnsafe) + if (name.IsPrefetchMemory && mmType == MemoryManagerType.HostTrackedUnsafe) { // Prefetch to invalid addresses do not cause faults, so for memory manager // types where we need to access the page table before doing the prefetch, @@ -544,7 +544,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 { Operand basePointer = new(regAlloc.FixedPageTableRegister, RegisterType.Integer, OperandType.I64); - if (mmType.IsHostTracked()) + if (mmType.IsHostTracked) { int tempRegister = regAlloc.AllocateTempGprRegister(); @@ -562,7 +562,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 regAlloc.FreeTempGprRegister(tempRegister); } - else if (mmType.IsHostMapped()) + else if (mmType.IsHostMapped) { if (mmType == MemoryManagerType.HostMapped) { diff --git a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/ArmCondition.cs b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/ArmCondition.cs index caa2e593b..9293b497a 100644 --- a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/ArmCondition.cs +++ b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/ArmCondition.cs @@ -22,9 +22,9 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 static class ArmConditionExtensions { - public static ArmCondition Invert(this ArmCondition condition) + extension(ArmCondition condition) { - return (ArmCondition)((int)condition ^ 1); + public ArmCondition Inverse => (ArmCondition)((int)condition ^ 1); } } } diff --git a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/Assembler.cs b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/Assembler.cs index 3eeda20bf..f6eb226ec 100644 --- a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/Assembler.cs +++ b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/Assembler.cs @@ -673,7 +673,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 public readonly void Mov(Operand rd, Operand rn) { - Debug.Assert(rd.Type.IsInteger()); + Debug.Assert(rd.Type.IsInteger); Orr(rd, new Operand(ZrRegister, RegisterType.Integer, rd.Type), rn); } @@ -4544,7 +4544,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 uint instruction; int scale; - if (type.IsInteger()) + if (type.IsInteger) { instruction = intInst; @@ -4580,7 +4580,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 { uint instruction; - if (type.IsInteger()) + if (type.IsInteger) { instruction = intInst; @@ -4610,7 +4610,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 { uint instruction; - if (type.IsInteger()) + if (type.IsInteger) { instruction = intInst; diff --git a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/RegisterSaveRestore.cs b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/RegisterSaveRestore.cs index b4b8bb524..527448e1c 100644 --- a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/RegisterSaveRestore.cs +++ b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/RegisterSaveRestore.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 int gprCalleeSavedRegsCount = BitOperations.PopCount(_gprMask); int fpSimdCalleeSavedRegsCount = BitOperations.PopCount(_fpSimdMask); - return (_hasCall ? 16 : 0) + Align16(gprCalleeSavedRegsCount * 8 + fpSimdCalleeSavedRegsCount * _fpSimdType.GetSizeInBytes()); + return (_hasCall ? 16 : 0) + Align16(gprCalleeSavedRegsCount * 8 + fpSimdCalleeSavedRegsCount * _fpSimdType.ByteSize); } public void WritePrologue(ref Assembler asm) @@ -46,7 +46,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 int fpSimdCalleeSavedRegsCount = BitOperations.PopCount(fpSimdMask); int reservedStackSize = Align16(_reservedStackSize); - int calleeSaveRegionSize = Align16(gprCalleeSavedRegsCount * 8 + fpSimdCalleeSavedRegsCount * _fpSimdType.GetSizeInBytes()) + reservedStackSize; + int calleeSaveRegionSize = Align16(gprCalleeSavedRegsCount * 8 + fpSimdCalleeSavedRegsCount * _fpSimdType.ByteSize) + reservedStackSize; int offset = 0; WritePrologueCalleeSavesPreIndexed(ref asm, ref gprMask, ref offset, calleeSaveRegionSize, OperandType.I64); @@ -103,7 +103,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 asm.StrRiUn(Register(reg, type), Register(Assembler.SpRegister), 0); } - offset += type.GetSizeInBytes(); + offset += type.ByteSize; } while (mask != 0) @@ -130,7 +130,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 asm.StpRiUn(Register(reg, type), Register(reg2, type), Register(Assembler.SpRegister), 0); } - offset += type.GetSizeInBytes() * 2; + offset += type.ByteSize * 2; } } @@ -144,7 +144,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 bool misalignedVector = _fpSimdType == OperandType.V128 && (gprCalleeSavedRegsCount & 1) != 0; - int offset = gprCalleeSavedRegsCount * 8 + fpSimdCalleeSavedRegsCount * _fpSimdType.GetSizeInBytes(); + int offset = gprCalleeSavedRegsCount * 8 + fpSimdCalleeSavedRegsCount * _fpSimdType.ByteSize; if (misalignedVector) { @@ -197,7 +197,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 mask &= ~(1u << reg2); - offset -= type.GetSizeInBytes() * 2; + offset -= type.ByteSize * 2; if (offset != 0) { @@ -215,7 +215,7 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 } else { - offset -= type.GetSizeInBytes(); + offset -= type.ByteSize; if (offset != 0) { diff --git a/src/Ryujinx.Cpu/LightningJit/CodeGen/OperandType.cs b/src/Ryujinx.Cpu/LightningJit/CodeGen/OperandType.cs index cd36c6781..ddb33167e 100644 --- a/src/Ryujinx.Cpu/LightningJit/CodeGen/OperandType.cs +++ b/src/Ryujinx.Cpu/LightningJit/CodeGen/OperandType.cs @@ -14,14 +14,11 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen static class OperandTypeExtensions { - public static bool IsInteger(this OperandType type) + extension(OperandType type) { - return type is OperandType.I32 or OperandType.I64; - } + public bool IsInteger => type is OperandType.I32 or OperandType.I64; - public static int GetSizeInBytes(this OperandType type) - { - return type switch + public int ByteSize => type switch { OperandType.FP32 => 4, OperandType.FP64 => 8, diff --git a/src/Ryujinx.Cpu/LightningJit/Translator.cs b/src/Ryujinx.Cpu/LightningJit/Translator.cs index f3ae0a9c5..1ee6993d2 100644 --- a/src/Ryujinx.Cpu/LightningJit/Translator.cs +++ b/src/Ryujinx.Cpu/LightningJit/Translator.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Cpu.LightningJit FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; - if (memory.Type.IsHostMappedOrTracked()) + if (memory.Type.IsHostMappedOrTracked) { NativeSignalHandler.InitializeSignalHandler(); } diff --git a/src/Ryujinx.Graphics.GAL/BlendFactor.cs b/src/Ryujinx.Graphics.GAL/BlendFactor.cs index 1dba229d6..99f946c3c 100644 --- a/src/Ryujinx.Graphics.GAL/BlendFactor.cs +++ b/src/Ryujinx.Graphics.GAL/BlendFactor.cs @@ -41,22 +41,12 @@ namespace Ryujinx.Graphics.GAL public static class BlendFactorExtensions { - public static bool IsDualSource(this BlendFactor factor) + extension(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; - default: - return false; - } + public bool IsDualSource => factor is + BlendFactor.Src1Color or BlendFactor.Src1ColorGl or BlendFactor.Src1Alpha or BlendFactor.Src1AlphaGl + or BlendFactor.OneMinusSrc1Color or BlendFactor.OneMinusSrc1ColorGl or BlendFactor.OneMinusSrc1Alpha + or BlendFactor.OneMinusSrc1AlphaGl; } } } diff --git a/src/Ryujinx.Graphics.GAL/BufferHandle.cs b/src/Ryujinx.Graphics.GAL/BufferHandle.cs index 7994e4eea..b8550a848 100644 --- a/src/Ryujinx.Graphics.GAL/BufferHandle.cs +++ b/src/Ryujinx.Graphics.GAL/BufferHandle.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.GAL @@ -10,5 +11,7 @@ namespace Ryujinx.Graphics.GAL public static BufferHandle Null => new(0); private BufferHandle(ulong value) => _value = value; + + public static implicit operator int(BufferHandle handle) => (int)Unsafe.As(ref handle); } } diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index 25446f978..277276fe3 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -159,589 +159,191 @@ namespace Ryujinx.Graphics.GAL /// public const int MaxBufferFormatScalarSize = 4; - /// - /// Gets the byte size for a single component of this format, or its packed size. - /// - /// Texture format - /// Byte size for a single component, or packed size - public static int GetScalarSize(this Format format) + extension(Format fmt) { - switch (format) + /// + /// Gets the byte size for a single component of this format, or its packed size. + /// + public int ScalarSize => fmt switch { - case Format.R8Unorm: - case Format.R8Snorm: - case Format.R8Uint: - case Format.R8Sint: - case Format.R8G8Unorm: - case Format.R8G8Snorm: - case Format.R8G8Uint: - case Format.R8G8Sint: - case Format.R8G8B8Unorm: - case Format.R8G8B8Snorm: - case Format.R8G8B8Uint: - case Format.R8G8B8Sint: - case Format.R8G8B8A8Unorm: - case Format.R8G8B8A8Snorm: - case Format.R8G8B8A8Uint: - case Format.R8G8B8A8Sint: - case Format.R8G8B8A8Srgb: - case Format.R4G4Unorm: - case Format.R8Uscaled: - case Format.R8Sscaled: - case Format.R8G8Uscaled: - case Format.R8G8Sscaled: - case Format.R8G8B8Uscaled: - case Format.R8G8B8Sscaled: - case Format.R8G8B8A8Uscaled: - case Format.R8G8B8A8Sscaled: - case Format.B8G8R8A8Unorm: - case Format.B8G8R8A8Srgb: - return 1; + Format.R8Unorm or Format.R8Snorm or Format.R8Uint or Format.R8Sint or Format.R8G8Unorm + or Format.R8G8Snorm or Format.R8G8Uint or Format.R8G8Sint or Format.R8G8B8Unorm + or Format.R8G8B8Snorm or Format.R8G8B8Uint or Format.R8G8B8Sint or Format.R8G8B8A8Unorm + or Format.R8G8B8A8Snorm or Format.R8G8B8A8Uint or Format.R8G8B8A8Sint or Format.R8G8B8A8Srgb + or Format.R4G4Unorm or Format.R8Uscaled or Format.R8Sscaled or Format.R8G8Uscaled + or Format.R8G8Sscaled or Format.R8G8B8Uscaled or Format.R8G8B8Sscaled or Format.R8G8B8A8Uscaled + or Format.R8G8B8A8Sscaled or Format.B8G8R8A8Unorm or Format.B8G8R8A8Srgb => 1, + Format.R16Float or Format.R16Unorm or Format.R16Snorm or Format.R16Uint or Format.R16Sint + or Format.R16G16Float or Format.R16G16Unorm or Format.R16G16Snorm or Format.R16G16Uint + or Format.R16G16Sint or Format.R16G16B16Float or Format.R16G16B16Unorm or Format.R16G16B16Snorm + or Format.R16G16B16Uint or Format.R16G16B16Sint or Format.R16G16B16A16Float + or Format.R16G16B16A16Unorm or Format.R16G16B16A16Snorm or Format.R16G16B16A16Uint + or Format.R16G16B16A16Sint or Format.R4G4B4A4Unorm or Format.R5G5B5X1Unorm or Format.R5G5B5A1Unorm + or Format.R5G6B5Unorm or Format.R16Uscaled or Format.R16Sscaled or Format.R16G16Uscaled + or Format.R16G16Sscaled or Format.R16G16B16Uscaled or Format.R16G16B16Sscaled + or Format.R16G16B16A16Uscaled or Format.R16G16B16A16Sscaled or Format.B5G6R5Unorm + or Format.B5G5R5A1Unorm or Format.A1B5G5R5Unorm => 2, + Format.R32Float or Format.R32Uint or Format.R32Sint or Format.R32G32Float or Format.R32G32Uint + or Format.R32G32Sint or Format.R32G32B32Float or Format.R32G32B32Uint or Format.R32G32B32Sint + or Format.R32G32B32A32Float or Format.R32G32B32A32Uint or Format.R32G32B32A32Sint + or Format.R10G10B10A2Unorm or Format.R10G10B10A2Uint or Format.R11G11B10Float + or Format.R9G9B9E5Float or Format.R32Uscaled or Format.R32Sscaled or Format.R32G32Uscaled + or Format.R32G32Sscaled or Format.R32G32B32Uscaled or Format.R32G32B32Sscaled + or Format.R32G32B32A32Uscaled or Format.R32G32B32A32Sscaled or Format.R10G10B10A2Snorm + or Format.R10G10B10A2Sint or Format.R10G10B10A2Uscaled or Format.R10G10B10A2Sscaled + or Format.B10G10R10A2Unorm => 4, + Format.S8Uint => 1, + Format.D16Unorm => 2, + Format.S8UintD24Unorm or Format.X8UintD24Unorm or Format.D32Float or Format.D24UnormS8Uint => 4, + Format.D32FloatS8Uint => 8, + Format.Bc1RgbaUnorm or Format.Bc1RgbaSrgb => 8, + Format.Bc2Unorm or Format.Bc3Unorm or Format.Bc2Srgb or Format.Bc3Srgb or Format.Bc4Unorm + or Format.Bc4Snorm or Format.Bc5Unorm or Format.Bc5Snorm or Format.Bc7Unorm or Format.Bc7Srgb + or Format.Bc6HSfloat or Format.Bc6HUfloat => 16, + Format.Etc2RgbUnorm or Format.Etc2RgbPtaUnorm or Format.Etc2RgbSrgb or Format.Etc2RgbPtaSrgb => 8, + Format.Etc2RgbaUnorm or Format.Etc2RgbaSrgb => 16, + Format.Astc4x4Unorm or Format.Astc5x4Unorm or Format.Astc5x5Unorm or Format.Astc6x5Unorm + or Format.Astc6x6Unorm or Format.Astc8x5Unorm or Format.Astc8x6Unorm or Format.Astc8x8Unorm + or Format.Astc10x5Unorm or Format.Astc10x6Unorm or Format.Astc10x8Unorm or Format.Astc10x10Unorm + or Format.Astc12x10Unorm or Format.Astc12x12Unorm or Format.Astc4x4Srgb or Format.Astc5x4Srgb + or Format.Astc5x5Srgb or Format.Astc6x5Srgb or Format.Astc6x6Srgb or Format.Astc8x5Srgb + or Format.Astc8x6Srgb or Format.Astc8x8Srgb or Format.Astc10x5Srgb or Format.Astc10x6Srgb + or Format.Astc10x8Srgb or Format.Astc10x10Srgb or Format.Astc12x10Srgb + or Format.Astc12x12Srgb => 16, + _ => 1 + }; - case Format.R16Float: - case Format.R16Unorm: - case Format.R16Snorm: - case Format.R16Uint: - case Format.R16Sint: - case Format.R16G16Float: - case Format.R16G16Unorm: - case Format.R16G16Snorm: - case Format.R16G16Uint: - case Format.R16G16Sint: - case Format.R16G16B16Float: - case Format.R16G16B16Unorm: - case Format.R16G16B16Snorm: - case Format.R16G16B16Uint: - case Format.R16G16B16Sint: - case Format.R16G16B16A16Float: - case Format.R16G16B16A16Unorm: - case Format.R16G16B16A16Snorm: - case Format.R16G16B16A16Uint: - case Format.R16G16B16A16Sint: - case Format.R4G4B4A4Unorm: - case Format.R5G5B5X1Unorm: - case Format.R5G5B5A1Unorm: - case Format.R5G6B5Unorm: - case Format.R16Uscaled: - case Format.R16Sscaled: - case Format.R16G16Uscaled: - case Format.R16G16Sscaled: - case Format.R16G16B16Uscaled: - case Format.R16G16B16Sscaled: - case Format.R16G16B16A16Uscaled: - case Format.R16G16B16A16Sscaled: - case Format.B5G6R5Unorm: - case Format.B5G5R5A1Unorm: - case Format.A1B5G5R5Unorm: - return 2; + /// + /// Checks if the texture format is a depth or depth-stencil format. + /// + public bool HasDepth => fmt is + Format.D16Unorm or Format.D24UnormS8Uint or Format.S8UintD24Unorm or Format.X8UintD24Unorm + or Format.D32Float or Format.D32FloatS8Uint; - case Format.R32Float: - case Format.R32Uint: - case Format.R32Sint: - case Format.R32G32Float: - case Format.R32G32Uint: - case Format.R32G32Sint: - case Format.R32G32B32Float: - case Format.R32G32B32Uint: - case Format.R32G32B32Sint: - case Format.R32G32B32A32Float: - case Format.R32G32B32A32Uint: - case Format.R32G32B32A32Sint: - case Format.R10G10B10A2Unorm: - case Format.R10G10B10A2Uint: - case Format.R11G11B10Float: - case Format.R9G9B9E5Float: - case Format.R32Uscaled: - case Format.R32Sscaled: - case Format.R32G32Uscaled: - case Format.R32G32Sscaled: - case Format.R32G32B32Uscaled: - case Format.R32G32B32Sscaled: - case Format.R32G32B32A32Uscaled: - case Format.R32G32B32A32Sscaled: - case Format.R10G10B10A2Snorm: - case Format.R10G10B10A2Sint: - case Format.R10G10B10A2Uscaled: - case Format.R10G10B10A2Sscaled: - case Format.B10G10R10A2Unorm: - return 4; + /// + /// Checks if the texture format is a stencil or depth-stencil format. + /// + public bool HasStencil => fmt is + Format.D24UnormS8Uint or Format.S8UintD24Unorm or Format.D32FloatS8Uint or Format.S8Uint; - case Format.S8Uint: - return 1; - case Format.D16Unorm: - return 2; - case Format.S8UintD24Unorm: - case Format.X8UintD24Unorm: - case Format.D32Float: - case Format.D24UnormS8Uint: - return 4; - case Format.D32FloatS8Uint: - return 8; + /// + /// Checks if the texture format is valid to use as image format. + /// + public bool IsImageCompatible => fmt is + Format.R8Unorm or Format.R8Snorm or Format.R8Uint or Format.R8Sint or Format.R16Float or Format.R16Unorm + or Format.R16Snorm or Format.R16Uint or Format.R16Sint or Format.R32Float or Format.R32Uint + or Format.R32Sint or Format.R8G8Unorm or Format.R8G8Snorm or Format.R8G8Uint or Format.R8G8Sint + or Format.R16G16Float or Format.R16G16Unorm or Format.R16G16Snorm or Format.R16G16Uint + or Format.R16G16Sint or Format.R32G32Float or Format.R32G32Uint or Format.R32G32Sint + or Format.R8G8B8A8Unorm or Format.R8G8B8A8Snorm or Format.R8G8B8A8Uint or Format.R8G8B8A8Sint + or Format.R16G16B16A16Float or Format.R16G16B16A16Unorm or Format.R16G16B16A16Snorm + or Format.R16G16B16A16Uint or Format.R16G16B16A16Sint or Format.R32G32B32A32Float + or Format.R32G32B32A32Uint or Format.R32G32B32A32Sint or Format.R10G10B10A2Unorm + or Format.R10G10B10A2Uint or Format.R11G11B10Float or Format.B8G8R8A8Unorm; - case Format.Bc1RgbaUnorm: - case Format.Bc1RgbaSrgb: - return 8; + /// + /// Checks if the texture format is valid to use as render target color format. + /// + public bool IsRtColorCompatible => fmt is + Format.R32G32B32A32Float or Format.R32G32B32A32Sint or Format.R32G32B32A32Uint + or Format.R16G16B16A16Unorm or Format.R16G16B16A16Snorm or Format.R16G16B16A16Sint + or Format.R16G16B16A16Uint or Format.R16G16B16A16Float or Format.R32G32Float or Format.R32G32Sint + or Format.R32G32Uint or Format.B8G8R8A8Unorm or Format.B8G8R8A8Srgb or Format.B10G10R10A2Unorm + or Format.R10G10B10A2Unorm or Format.R10G10B10A2Uint or Format.R8G8B8A8Unorm or Format.R8G8B8A8Srgb + or Format.R8G8B8A8Snorm or Format.R8G8B8A8Sint or Format.R8G8B8A8Uint or Format.R16G16Unorm + or Format.R16G16Snorm or Format.R16G16Sint or Format.R16G16Uint or Format.R16G16Float + or Format.R11G11B10Float or Format.R32Sint or Format.R32Uint or Format.R32Float + or Format.B5G6R5Unorm or Format.B5G5R5A1Unorm or Format.R8G8Unorm or Format.R8G8Snorm + or Format.R8G8Sint or Format.R8G8Uint or Format.R16Unorm or Format.R16Snorm or Format.R16Sint + or Format.R16Uint or Format.R16Float or Format.R8Unorm or Format.R8Snorm or Format.R8Sint + or Format.R8Uint; - case Format.Bc2Unorm: - case Format.Bc3Unorm: - case Format.Bc2Srgb: - case Format.Bc3Srgb: - case Format.Bc4Unorm: - case Format.Bc4Snorm: - case Format.Bc5Unorm: - case Format.Bc5Snorm: - case Format.Bc7Unorm: - case Format.Bc7Srgb: - case Format.Bc6HSfloat: - case Format.Bc6HUfloat: - return 16; + /// + /// Checks if the texture format is 16 bit packed. + /// + public bool Is16BitPacked => fmt is + Format.B5G6R5Unorm or Format.B5G5R5A1Unorm or Format.R5G5B5X1Unorm or Format.R5G5B5A1Unorm + or Format.R5G6B5Unorm or Format.R4G4B4A4Unorm; - case Format.Etc2RgbUnorm: - case Format.Etc2RgbPtaUnorm: - case Format.Etc2RgbSrgb: - case Format.Etc2RgbPtaSrgb: - return 8; + /// + /// Checks if the texture format is an ETC2 format. + /// + public bool IsEtc2 => fmt is + Format.Etc2RgbaSrgb or Format.Etc2RgbaUnorm or Format.Etc2RgbPtaSrgb + or Format.Etc2RgbPtaUnorm or Format.Etc2RgbSrgb or Format.Etc2RgbUnorm; - case Format.Etc2RgbaUnorm: - case Format.Etc2RgbaSrgb: - return 16; + /// + /// Checks if the texture format is a BGR format. + /// + public bool IsBgr => fmt is + Format.B5G6R5Unorm or Format.B5G5R5A1Unorm or Format.B8G8R8A8Unorm or Format.B8G8R8A8Srgb + or Format.B10G10R10A2Unorm; - case Format.Astc4x4Unorm: - case Format.Astc5x4Unorm: - case Format.Astc5x5Unorm: - case Format.Astc6x5Unorm: - case Format.Astc6x6Unorm: - case Format.Astc8x5Unorm: - case Format.Astc8x6Unorm: - case Format.Astc8x8Unorm: - case Format.Astc10x5Unorm: - case Format.Astc10x6Unorm: - case Format.Astc10x8Unorm: - case Format.Astc10x10Unorm: - case Format.Astc12x10Unorm: - case Format.Astc12x12Unorm: - case Format.Astc4x4Srgb: - case Format.Astc5x4Srgb: - case Format.Astc5x5Srgb: - case Format.Astc6x5Srgb: - case Format.Astc6x6Srgb: - case Format.Astc8x5Srgb: - case Format.Astc8x6Srgb: - case Format.Astc8x8Srgb: - case Format.Astc10x5Srgb: - case Format.Astc10x6Srgb: - case Format.Astc10x8Srgb: - case Format.Astc10x10Srgb: - case Format.Astc12x10Srgb: - case Format.Astc12x12Srgb: - return 16; - } + /// + /// Checks if the texture format is a depth, stencil or depth-stencil format. + /// + public bool IsDepthOrStencil => fmt is + Format.D16Unorm or Format.D24UnormS8Uint or Format.S8UintD24Unorm or Format.X8UintD24Unorm + or Format.D32Float or Format.D32FloatS8Uint or Format.S8Uint; - return 1; - } + /// + /// Checks if the texture format is a float or sRGB color format. + /// + /// + /// Does not include normalized, compressed or depth formats. + /// Float and sRGB formats do not participate in logical operations. + /// + public bool IsFloatOrSrgb => fmt is + Format.R8G8B8A8Srgb or Format.B8G8R8A8Srgb or Format.R16Float or Format.R16G16Float + or Format.R16G16B16Float or Format.R16G16B16A16Float or Format.R32Float or Format.R32G32Float + or Format.R32G32B32Float or Format.R32G32B32A32Float or Format.R11G11B10Float + or Format.R9G9B9E5Float; + + /// + /// Checks if the texture format is an ASTC Unorm format. + /// + public bool IsAstcUnorm => fmt is + Format.Astc4x4Unorm or Format.Astc5x4Unorm or Format.Astc5x5Unorm or Format.Astc6x5Unorm + or Format.Astc6x6Unorm or Format.Astc8x5Unorm or Format.Astc8x6Unorm or Format.Astc8x8Unorm + or Format.Astc10x5Unorm or Format.Astc10x6Unorm or Format.Astc10x8Unorm or Format.Astc10x10Unorm + or Format.Astc12x10Unorm or Format.Astc12x12Unorm; - /// - /// Checks if the texture format is a depth or depth-stencil format. - /// - /// Texture format - /// True if the format is a depth or depth-stencil format, false otherwise - public static bool HasDepth(this Format format) - { - switch (format) - { - case Format.D16Unorm: - case Format.D24UnormS8Uint: - case Format.S8UintD24Unorm: - case Format.X8UintD24Unorm: - case Format.D32Float: - case Format.D32FloatS8Uint: - return true; - } + /// + /// Checks if the texture format is an ASTC SRGB format. + /// + public bool IsAstcSrgb => fmt is + Format.Astc4x4Srgb or Format.Astc5x4Srgb or Format.Astc5x5Srgb or Format.Astc6x5Srgb + or Format.Astc6x6Srgb or Format.Astc8x5Srgb or Format.Astc8x6Srgb or Format.Astc8x8Srgb + or Format.Astc10x5Srgb or Format.Astc10x6Srgb or Format.Astc10x8Srgb or Format.Astc10x10Srgb + or Format.Astc12x10Srgb or Format.Astc12x12Srgb; - return false; - } + /// + /// Checks if the texture format is an ASTC format. + /// + public bool IsAstc => fmt.IsAstcUnorm || fmt.IsAstcSrgb; - /// - /// Checks if the texture format is a stencil or depth-stencil format. - /// - /// Texture format - /// True if the format is a stencil or depth-stencil format, false otherwise - public static bool HasStencil(this Format format) - { - switch (format) - { - case Format.D24UnormS8Uint: - case Format.S8UintD24Unorm: - case Format.D32FloatS8Uint: - case Format.S8Uint: - return true; - } + /// + /// Checks if the texture format is an unsigned integer color format. + /// + public bool IsUnsignedInt => fmt is + Format.R8Uint or Format.R16Uint or Format.R32Uint or Format.R8G8Uint or Format.R16G16Uint + or Format.R32G32Uint or Format.R8G8B8Uint or Format.R16G16B16Uint or Format.R32G32B32Uint + or Format.R8G8B8A8Uint or Format.R16G16B16A16Uint or Format.R32G32B32A32Uint + or Format.R10G10B10A2Uint; - return false; - } + /// + /// Checks if the texture format is a signed integer color format. + /// + public bool IsSignedInt => fmt is + Format.R8Sint or Format.R16Sint or Format.R32Sint or Format.R8G8Sint or Format.R16G16Sint + or Format.R32G32Sint or Format.R8G8B8Sint or Format.R16G16B16Sint or Format.R32G32B32Sint + or Format.R8G8B8A8Sint or Format.R16G16B16A16Sint or Format.R32G32B32A32Sint + or Format.R10G10B10A2Sint; - /// - /// Checks if the texture format is valid to use as image format. - /// - /// Texture format - /// True if the texture can be used as image, false otherwise - public static bool IsImageCompatible(this Format format) - { - switch (format) - { - case Format.R8Unorm: - case Format.R8Snorm: - case Format.R8Uint: - case Format.R8Sint: - case Format.R16Float: - case Format.R16Unorm: - case Format.R16Snorm: - case Format.R16Uint: - case Format.R16Sint: - case Format.R32Float: - case Format.R32Uint: - case Format.R32Sint: - case Format.R8G8Unorm: - case Format.R8G8Snorm: - case Format.R8G8Uint: - case Format.R8G8Sint: - case Format.R16G16Float: - case Format.R16G16Unorm: - case Format.R16G16Snorm: - case Format.R16G16Uint: - case Format.R16G16Sint: - case Format.R32G32Float: - case Format.R32G32Uint: - case Format.R32G32Sint: - case Format.R8G8B8A8Unorm: - case Format.R8G8B8A8Snorm: - case Format.R8G8B8A8Uint: - case Format.R8G8B8A8Sint: - case Format.R16G16B16A16Float: - case Format.R16G16B16A16Unorm: - case Format.R16G16B16A16Snorm: - case Format.R16G16B16A16Uint: - case Format.R16G16B16A16Sint: - case Format.R32G32B32A32Float: - case Format.R32G32B32A32Uint: - case Format.R32G32B32A32Sint: - case Format.R10G10B10A2Unorm: - case Format.R10G10B10A2Uint: - case Format.R11G11B10Float: - case Format.B8G8R8A8Unorm: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is valid to use as render target color format. - /// - /// Texture format - /// True if the texture can be used as render target, false otherwise - public static bool IsRtColorCompatible(this Format format) - { - switch (format) - { - case Format.R32G32B32A32Float: - case Format.R32G32B32A32Sint: - case Format.R32G32B32A32Uint: - case Format.R16G16B16A16Unorm: - case Format.R16G16B16A16Snorm: - case Format.R16G16B16A16Sint: - case Format.R16G16B16A16Uint: - case Format.R16G16B16A16Float: - case Format.R32G32Float: - case Format.R32G32Sint: - case Format.R32G32Uint: - case Format.B8G8R8A8Unorm: - case Format.B8G8R8A8Srgb: - case Format.B10G10R10A2Unorm: - case Format.R10G10B10A2Unorm: - case Format.R10G10B10A2Uint: - case Format.R8G8B8A8Unorm: - case Format.R8G8B8A8Srgb: - case Format.R8G8B8A8Snorm: - case Format.R8G8B8A8Sint: - case Format.R8G8B8A8Uint: - case Format.R16G16Unorm: - case Format.R16G16Snorm: - case Format.R16G16Sint: - case Format.R16G16Uint: - case Format.R16G16Float: - case Format.R11G11B10Float: - case Format.R32Sint: - case Format.R32Uint: - case Format.R32Float: - case Format.B5G6R5Unorm: - case Format.B5G5R5A1Unorm: - case Format.R8G8Unorm: - case Format.R8G8Snorm: - case Format.R8G8Sint: - case Format.R8G8Uint: - case Format.R16Unorm: - case Format.R16Snorm: - case Format.R16Sint: - case Format.R16Uint: - case Format.R16Float: - case Format.R8Unorm: - case Format.R8Snorm: - case Format.R8Sint: - case Format.R8Uint: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is 16 bit packed. - /// - /// Texture format - /// True if the texture format is 16 bit packed, false otherwise - public static bool Is16BitPacked(this Format format) - { - switch (format) - { - case Format.B5G6R5Unorm: - case Format.B5G5R5A1Unorm: - case Format.R5G5B5X1Unorm: - case Format.R5G5B5A1Unorm: - case Format.R5G6B5Unorm: - case Format.R4G4B4A4Unorm: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is an ASTC format. - /// - /// Texture format - /// True if the texture format is an ASTC format, false otherwise - public static bool IsAstc(this Format format) - { - return format.IsAstcUnorm() || format.IsAstcSrgb(); - } - - /// - /// Checks if the texture format is an ASTC Unorm format. - /// - /// Texture format - /// True if the texture format is an ASTC Unorm format, false otherwise - public static bool IsAstcUnorm(this Format format) - { - switch (format) - { - case Format.Astc4x4Unorm: - case Format.Astc5x4Unorm: - case Format.Astc5x5Unorm: - case Format.Astc6x5Unorm: - case Format.Astc6x6Unorm: - case Format.Astc8x5Unorm: - case Format.Astc8x6Unorm: - case Format.Astc8x8Unorm: - case Format.Astc10x5Unorm: - case Format.Astc10x6Unorm: - case Format.Astc10x8Unorm: - case Format.Astc10x10Unorm: - case Format.Astc12x10Unorm: - case Format.Astc12x12Unorm: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is an ASTC SRGB format. - /// - /// Texture format - /// True if the texture format is an ASTC SRGB format, false otherwise - public static bool IsAstcSrgb(this Format format) - { - switch (format) - { - case Format.Astc4x4Srgb: - case Format.Astc5x4Srgb: - case Format.Astc5x5Srgb: - case Format.Astc6x5Srgb: - case Format.Astc6x6Srgb: - case Format.Astc8x5Srgb: - case Format.Astc8x6Srgb: - case Format.Astc8x8Srgb: - case Format.Astc10x5Srgb: - case Format.Astc10x6Srgb: - case Format.Astc10x8Srgb: - case Format.Astc10x10Srgb: - case Format.Astc12x10Srgb: - case Format.Astc12x12Srgb: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is an ETC2 format. - /// - /// Texture format - /// True if the texture format is an ETC2 format, false otherwise - public static bool IsEtc2(this Format format) - { - switch (format) - { - case Format.Etc2RgbaSrgb: - case Format.Etc2RgbaUnorm: - case Format.Etc2RgbPtaSrgb: - case Format.Etc2RgbPtaUnorm: - case Format.Etc2RgbSrgb: - case Format.Etc2RgbUnorm: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is a BGR format. - /// - /// Texture format - /// True if the texture format is a BGR format, false otherwise - public static bool IsBgr(this Format format) - { - switch (format) - { - case Format.B5G6R5Unorm: - case Format.B5G5R5A1Unorm: - case Format.B8G8R8A8Unorm: - case Format.B8G8R8A8Srgb: - case Format.B10G10R10A2Unorm: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is a depth, stencil or depth-stencil format. - /// - /// Texture format - /// True if the format is a depth, stencil or depth-stencil format, false otherwise - public static bool IsDepthOrStencil(this Format format) - { - switch (format) - { - case Format.D16Unorm: - case Format.D24UnormS8Uint: - case Format.S8UintD24Unorm: - case Format.X8UintD24Unorm: - case Format.D32Float: - case Format.D32FloatS8Uint: - case Format.S8Uint: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is an unsigned integer color format. - /// - /// Texture format - /// True if the texture format is an unsigned integer color format, false otherwise - public static bool IsUint(this Format format) - { - switch (format) - { - case Format.R8Uint: - case Format.R16Uint: - case Format.R32Uint: - case Format.R8G8Uint: - case Format.R16G16Uint: - case Format.R32G32Uint: - case Format.R8G8B8Uint: - case Format.R16G16B16Uint: - case Format.R32G32B32Uint: - case Format.R8G8B8A8Uint: - case Format.R16G16B16A16Uint: - case Format.R32G32B32A32Uint: - case Format.R10G10B10A2Uint: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is a signed integer color format. - /// - /// Texture format - /// True if the texture format is a signed integer color format, false otherwise - public static bool IsSint(this Format format) - { - switch (format) - { - case Format.R8Sint: - case Format.R16Sint: - case Format.R32Sint: - case Format.R8G8Sint: - case Format.R16G16Sint: - case Format.R32G32Sint: - case Format.R8G8B8Sint: - case Format.R16G16B16Sint: - case Format.R32G32B32Sint: - case Format.R8G8B8A8Sint: - case Format.R16G16B16A16Sint: - case Format.R32G32B32A32Sint: - case Format.R10G10B10A2Sint: - return true; - } - - return false; - } - - /// - /// Checks if the texture format is an integer color format. - /// - /// Texture format - /// True if the texture format is an integer color format, false otherwise - public static bool IsInteger(this Format format) - { - return format.IsUint() || format.IsSint(); - } - - /// - /// Checks if the texture format is a float or sRGB color format. - /// - /// - /// Does not include normalized, compressed or depth formats. - /// Float and sRGB formats do not participate in logical operations. - /// - /// Texture format - /// True if the format is a float or sRGB color format, false otherwise - public static bool IsFloatOrSrgb(this Format format) - { - switch (format) - { - case Format.R8G8B8A8Srgb: - case Format.B8G8R8A8Srgb: - case Format.R16Float: - case Format.R16G16Float: - case Format.R16G16B16Float: - case Format.R16G16B16A16Float: - case Format.R32Float: - case Format.R32G32Float: - case Format.R32G32B32Float: - case Format.R32G32B32A32Float: - case Format.R11G11B10Float: - case Format.R9G9B9E5Float: - return true; - } - - return false; + /// + /// Checks if the texture format is an integer color format. + /// + public bool IsInt => fmt.IsUnsignedInt || fmt.IsSignedInt; } } } diff --git a/src/Ryujinx.Graphics.GAL/Target.cs b/src/Ryujinx.Graphics.GAL/Target.cs index 1c184981d..531051d02 100644 --- a/src/Ryujinx.Graphics.GAL/Target.cs +++ b/src/Ryujinx.Graphics.GAL/Target.cs @@ -16,19 +16,18 @@ namespace Ryujinx.Graphics.GAL public static class TargetExtensions { - public static bool IsMultisample(this Target target) + extension(Target target) { - return target is Target.Texture2DMultisample or Target.Texture2DMultisampleArray; - } + public bool IsMultisample => target is Target.Texture2DMultisample or Target.Texture2DMultisampleArray; - public static bool HasDepthOrLayers(this Target target) - { - return target is Target.Texture3D or - Target.Texture1DArray or - Target.Texture2DArray or - Target.Texture2DMultisampleArray or - Target.Cubemap or - Target.CubemapArray; + public bool HasDepthOrLayers => + target is + Target.Texture3D or + Target.Texture1DArray or + Target.Texture2DArray or + Target.Texture2DMultisampleArray or + Target.Cubemap or + Target.CubemapArray; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs index 37d7457fc..c9fe2470e 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs @@ -171,7 +171,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory { MemoryManager memoryManager = _channel.MemoryManager; - Span data = MemoryMarshal.Cast(_buffer)[.._size]; + Span data = MemoryMarshal.Cast(new Span(_buffer))[.._size]; if (_isLinear && _lineCount == 1) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 23a73908d..6d62464d2 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -176,7 +176,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count); ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset(); - int componentSize = format.GetScalarSize(); + int componentSize = format.ScalarSize; address += attributeOffset; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index f97f80251..d9218e524 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -849,8 +849,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed FormatInfo dsFormat = _state.State.RtDepthStencilState.Format.Convert(); - bool hasDepth = dsFormat.Format.HasDepth(); - bool hasStencil = dsFormat.Format.HasStencil(); + bool hasDepth = dsFormat.Format.HasDepth; + bool hasStencil = dsFormat.Format.HasStencil; if (hasStencil && (!clearStencil || (clearAffectedByStencilMask && _state.State.StencilTestState.FrontMask != 0xff))) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs index 2fb8de920..0c1e33731 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs @@ -297,7 +297,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { Format format = colorState.Format.Convert().Format; - AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float; + AttributeType type = format.IsInt + ? (format.IsSignedInt ? AttributeType.Sint : AttributeType.Uint) + : AttributeType.Float; if (type != fragmentOutputTypesSpan[index]) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index fe57c05ee..4f2f01606 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -534,7 +534,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (!_context.Capabilities.SupportsBgraFormat) { - _context.SupportBufferUpdater.SetRenderTargetIsBgra(index, color.Format.IsBgr()); + _context.SupportBufferUpdater.SetRenderTargetIsBgra(index, color.Format.IsBgr); } } } @@ -1317,10 +1317,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed FilterBlendFactor(blend.AlphaDstFactor, index)); if (enable && - (blend.ColorSrcFactor.IsDualSource() || - blend.ColorDstFactor.IsDualSource() || - blend.AlphaSrcFactor.IsDualSource() || - blend.AlphaDstFactor.IsDualSource())) + (blend.ColorSrcFactor.IsDualSource || + blend.ColorDstFactor.IsDualSource || + blend.AlphaSrcFactor.IsDualSource || + blend.AlphaDstFactor.IsDualSource)) { dualSourceBlendEnabled = true; } @@ -1345,10 +1345,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed FilterBlendFactor(blend.AlphaDstFactor, 0)); if (enable && - (blend.ColorSrcFactor.IsDualSource() || - blend.ColorDstFactor.IsDualSource() || - blend.AlphaSrcFactor.IsDualSource() || - blend.AlphaDstFactor.IsDualSource())) + (blend.ColorSrcFactor.IsDualSource || + blend.ColorDstFactor.IsDualSource || + blend.AlphaSrcFactor.IsDualSource || + blend.AlphaDstFactor.IsDualSource)) { dualSourceBlendEnabled = true; } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs index 5ab58d7d1..5deddbadf 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs @@ -333,7 +333,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod // 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()) + if (srcTexture.Format.IsDepthOrStencil) { dstCopyTextureFormat = srcTexture.Info.FormatInfo; } diff --git a/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs index 8bfebe8a1..b1e46cfbe 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs @@ -662,7 +662,7 @@ namespace Ryujinx.Graphics.Gpu.Image bool found = _textureFormats.TryGetValue((TextureFormat)encoded, out format); - if (found && isPacked && !format.Format.IsDepthOrStencil()) + if (found && isPacked && !format.Format.IsDepthOrStencil) { // If the packed flag is set, then the components of the pixel are tightly packed into the // GPU registers on the shader. diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 35fc38d0b..05a316c45 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -643,7 +643,7 @@ namespace Ryujinx.Graphics.Gpu.Image // The decompression is slow, so we want to avoid it as much as possible. // This does a byte-by-byte check and skips the update if the data is equal in this case. // This improves the speed on applications that overwrites ASTC data without changing anything. - if (Info.FormatInfo.Format.IsAstc() && !_context.Capabilities.SupportsAstcCompression) + if (Info.FormatInfo.Format.IsAstc && !_context.Capabilities.SupportsAstcCompression) { if (_updateCount < ByteComparisonSwitchThreshold) { @@ -792,7 +792,7 @@ namespace Ryujinx.Graphics.Gpu.Image // Handle compressed cases not supported by the host: // - ASTC is usually not supported on desktop cards. // - BC4/BC5 is not supported on 3D textures. - if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc()) + if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc) { using (result) { @@ -823,7 +823,7 @@ namespace Ryujinx.Graphics.Gpu.Image return decoded; } } - else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2()) + else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2) { switch (Format) { @@ -924,7 +924,7 @@ namespace Ryujinx.Graphics.Gpu.Image } } } - else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked()) + else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked) { switch (Format) { @@ -1251,7 +1251,7 @@ namespace Ryujinx.Graphics.Gpu.Image { result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps)); - bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample(); + bool bothMs = Info.Target.IsMultisample && info.Target.IsMultisample; if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)) { result = TextureViewCompatibility.Incompatible; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index f3df8b072..390d7c5c7 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Gpu.Image int widthAlignment = (info.IsLinear ? Constants.StrideAlignment : Constants.GobAlignment) / info.FormatInfo.BytesPerPixel; - if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Components == 1)) + if (!(info.FormatInfo.Format.IsDepthOrStencil || info.FormatInfo.Components == 1)) { // Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas) // Detect if the texture is possibly square. Widths may be aligned, so to remove the uncertainty we align both the width and height. diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index f2cbca832..af7e9a7af 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -75,13 +75,14 @@ namespace Ryujinx.Graphics.Gpu.Image if (!caps.SupportsAstcCompression) { - if (info.FormatInfo.Format.IsAstcUnorm()) + if (info.FormatInfo.Format.IsAstcUnorm) { return GraphicsConfig.EnableTextureRecompression ? new FormatInfo(Format.Bc7Unorm, 4, 4, 16, 4) : new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4); } - else if (info.FormatInfo.Format.IsAstcSrgb()) + + if (info.FormatInfo.Format.IsAstcSrgb) { return GraphicsConfig.EnableTextureRecompression ? new FormatInfo(Format.Bc7Srgb, 4, 4, 16, 4) @@ -151,9 +152,9 @@ namespace Ryujinx.Graphics.Gpu.Image return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4); } } - else if (!caps.Supports5BitComponentFormat && info.FormatInfo.Format.Is16BitPacked()) + else if (!caps.Supports5BitComponentFormat && info.FormatInfo.Format.Is16BitPacked) { - return new FormatInfo(info.FormatInfo.Format.IsBgr() ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4); + return new FormatInfo(info.FormatInfo.Format.IsBgr ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4); } return info.FormatInfo; @@ -388,7 +389,7 @@ namespace Ryujinx.Graphics.Gpu.Image return stride == rhs.Stride ? TextureViewCompatibility.CopyOnly : TextureViewCompatibility.LayoutIncompatible; } - else if (lhs.Target.IsMultisample() != rhs.Target.IsMultisample() && alignedWidthMatches && lhsAlignedSize.Height == rhsAlignedSize.Height) + else if (lhs.Target.IsMultisample != rhs.Target.IsMultisample && alignedWidthMatches && lhsAlignedSize.Height == rhsAlignedSize.Height) { // Copy between multisample and non-multisample textures with mismatching size is allowed, // as long aligned size matches. @@ -644,7 +645,7 @@ namespace Ryujinx.Graphics.Gpu.Image FormatInfo lhsFormat = lhs.FormatInfo; FormatInfo rhsFormat = rhs.FormatInfo; - if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil()) + if (lhsFormat.Format.IsDepthOrStencil || rhsFormat.Format.IsDepthOrStencil) { bool forSampler = flags.HasFlag(TextureSearchFlags.ForSampler); bool depthAlias = flags.HasFlag(TextureSearchFlags.DepthAlias); diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 8865c46cc..e7a1afe1a 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -147,7 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Image _allOffsets = size.AllOffsets; _sliceSizes = size.SliceSizes; - if (Storage.Target.HasDepthOrLayers() && Storage.Info.GetSlices() > GranularLayerThreshold) + if (Storage.Target.HasDepthOrLayers && Storage.Info.GetSlices() > GranularLayerThreshold) { _hasLayerViews = true; _hasMipViews = true; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index c2a503840..d9fe02f8d 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -660,7 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Image swizzleB, swizzleA); - if (formatInfo.Format.IsDepthOrStencil()) + if (formatInfo.Format.IsDepthOrStencil) { swizzleR = SwizzleComponent.Red; swizzleG = SwizzleComponent.Red; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index 7861eb4fe..277a30689 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -138,7 +138,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (item.Value._useGranular) { - baseHandles.AddRange((item.Value._memoryTrackingGranular.GetHandles())); + baseHandles.AddRange(item.Value._memoryTrackingGranular.Handles); } else { diff --git a/src/Ryujinx.Graphics.OpenGL/Buffer.cs b/src/Ryujinx.Graphics.OpenGL/Buffer.cs index 33ca25174..9df408944 100644 --- a/src/Ryujinx.Graphics.OpenGL/Buffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Buffer.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.OpenGL { public static void Clear(BufferHandle destination, int offset, int size, uint value) { - GL.BindBuffer(BufferTarget.CopyWriteBuffer, destination.ToInt32()); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, destination); unsafe { @@ -58,8 +58,8 @@ namespace Ryujinx.Graphics.OpenGL public static void Copy(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) { - GL.BindBuffer(BufferTarget.CopyReadBuffer, source.ToInt32()); - GL.BindBuffer(BufferTarget.CopyWriteBuffer, destination.ToInt32()); + GL.BindBuffer(BufferTarget.CopyReadBuffer, source); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, destination); GL.CopyBufferSubData( BufferTarget.CopyReadBuffer, @@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.OpenGL { nint target = renderer.PersistentBuffers.Default.GetHostArray(size); - GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer.ToInt32()); + GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer); GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (nint)offset, size, target); @@ -96,13 +96,13 @@ namespace Ryujinx.Graphics.OpenGL public static void Resize(BufferHandle handle, int size) { - GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); GL.BufferData(BufferTarget.CopyWriteBuffer, size, nint.Zero, BufferUsageHint.StreamCopy); } public static void SetData(BufferHandle buffer, int offset, ReadOnlySpan data) { - GL.BindBuffer(BufferTarget.CopyWriteBuffer, buffer.ToInt32()); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, buffer); unsafe { @@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.OpenGL public static void Delete(BufferHandle buffer) { - GL.DeleteBuffer(buffer.ToInt32()); + GL.DeleteBuffer(buffer); } } } diff --git a/src/Ryujinx.Graphics.OpenGL/Handle.cs b/src/Ryujinx.Graphics.OpenGL/Handle.cs index b63e8f946..3803f0b07 100644 --- a/src/Ryujinx.Graphics.OpenGL/Handle.cs +++ b/src/Ryujinx.Graphics.OpenGL/Handle.cs @@ -1,4 +1,3 @@ -using Ryujinx.Graphics.GAL; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -14,10 +13,5 @@ namespace Ryujinx.Graphics.OpenGL return Unsafe.As(ref handle64); } - - public static int ToInt32(this BufferHandle handle) - { - return (int)Unsafe.As(ref handle); - } } } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs b/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs index 64e4fe36d..1a67c28e7 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs @@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.OpenGL.Image start = sizeAligned; } - Span outSpan = MemoryMarshal.Cast(output); + Span outSpan = MemoryMarshal.Cast(new Span(output)); ReadOnlySpan dataSpan = MemoryMarshal.Cast(data); for (int i = start / sizeof(uint); i < dataSpan.Length; i++) { diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index 231d9c97b..9ad4eb824 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.OpenGL.Image SizedInternalFormat format = (SizedInternalFormat)FormatTable.GetFormatInfo(Info.Format).PixelInternalFormat; - GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer.ToInt32(), (nint)buffer.Offset, buffer.Size); + GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer, (nint)buffer.Offset, buffer.Size); } public void Dispose() diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index 3d1e47339..4b6821934 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.OpenGL.Image int layers, int levels) { - TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src; + TextureView srcConverted = src.Format.IsBgr != dst.Format.IsBgr ? BgraSwap(src) : src; (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers(); @@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.OpenGL.Image ClearBufferMask mask = GetMask(src.Format); - if ((mask & (ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit)) != 0 || src.Format.IsInteger()) + if ((mask & (ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit)) != 0 || src.Format.IsInt) { linearFilter = false; } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index fcd004dd6..12ec23c8b 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.OpenGL.Image swizzleRgba[2] = temp2; swizzleRgba[3] = temp; } - else if (Info.Format.IsBgr()) + else if (Info.Format.IsBgr) { // 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. @@ -116,13 +116,13 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - bool srcIsMultisample = Target.IsMultisample(); - bool dstIsMultisample = destinationView.Target.IsMultisample(); + bool srcIsMultisample = Target.IsMultisample; + bool dstIsMultisample = destinationView.Target.IsMultisample; - if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil()) + if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil) { int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); - CopyWithBlitForDepthMS(destinationView, 0, firstLayer, layers); + CopyWithBlitForDepthMultisample(destinationView, 0, firstLayer, layers); } else if (!dstIsMultisample && srcIsMultisample) { @@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.OpenGL.Image int levels = Math.Min(Info.Levels, destinationView.Info.Levels - firstLevel); _renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, 0, firstLayer, 0, firstLevel, layers, levels); } - else if (destinationView.Format.IsDepthOrStencil() != Format.IsDepthOrStencil()) + else if (destinationView.Format.IsDepthOrStencil != Format.IsDepthOrStencil) { int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); int levels = Math.Min(Info.Levels, destinationView.Info.Levels - firstLevel); @@ -172,12 +172,12 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - bool srcIsMultisample = Target.IsMultisample(); - bool dstIsMultisample = destinationView.Target.IsMultisample(); + bool srcIsMultisample = Target.IsMultisample; + bool dstIsMultisample = destinationView.Target.IsMultisample; - if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil()) + if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil) { - CopyWithBlitForDepthMS(destinationView, srcLayer, dstLayer, 1); + CopyWithBlitForDepthMultisample(destinationView, srcLayer, dstLayer, 1); } else if (!dstIsMultisample && srcIsMultisample) { @@ -191,7 +191,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { _renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); } - else if (destinationView.Format.IsDepthOrStencil() != Format.IsDepthOrStencil()) + else if (destinationView.Format.IsDepthOrStencil != Format.IsDepthOrStencil) { int minWidth = Math.Min(Width, destinationView.Width); int minHeight = Math.Min(Height, destinationView.Height); @@ -204,7 +204,7 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - private void CopyWithBlitForDepthMS(TextureView destinationView, int srcLayer, int dstLayer, int layers) + private void CopyWithBlitForDepthMultisample(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. @@ -216,9 +216,9 @@ namespace Ryujinx.Graphics.OpenGL.Image Extents2D srcRegion = new(0, 0, Width, Height); Extents2D dstRegion = new(0, 0, destinationView.Width, destinationView.Height); - if (destinationView.Target.IsMultisample()) + if (destinationView.Target.IsMultisample) { - TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( + TextureView intermediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( Info.Target, Info.BlockWidth, Info.BlockHeight, @@ -230,8 +230,8 @@ namespace Ryujinx.Graphics.OpenGL.Image 1, 1); - _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, dstRegion, false); - _renderer.TextureCopy.Copy(intermmediate, destinationView, dstRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1); + _renderer.TextureCopy.Copy(this, intermediate, srcRegion, dstRegion, false); + _renderer.TextureCopy.Copy(intermediate, destinationView, dstRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1); } else { @@ -242,7 +242,7 @@ namespace Ryujinx.Graphics.OpenGL.Image _ => Target, }; - TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( + TextureView intermediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( target, Info.BlockWidth, Info.BlockHeight, @@ -254,8 +254,8 @@ namespace Ryujinx.Graphics.OpenGL.Image 1, 1); - _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, false); - _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1); + _renderer.TextureCopy.Copy(this, intermediate, srcRegion, srcRegion, false); + _renderer.TextureCopy.Copy(intermediate, destinationView, srcRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1); } } @@ -305,14 +305,12 @@ namespace Ryujinx.Graphics.OpenGL.Image { return PinnedSpan.UnsafeFromSpan(_renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level)); } - else - { - nint target = _renderer.PersistentBuffers.Default.GetHostArray(size); - int offset = WriteTo2D(target, layer, level); + nint target = _renderer.PersistentBuffers.Default.GetHostArray(size); - return new PinnedSpan((byte*)target.ToPointer() + offset, size); - } + int offset = WriteTo2D(target, layer, level); + + return new PinnedSpan((byte*)target.ToPointer() + offset, size); } public void CopyTo(BufferRange range, int layer, int level, int stride) @@ -322,7 +320,7 @@ namespace Ryujinx.Graphics.OpenGL.Image throw new NotSupportedException("Stride conversion for texture copy to buffer not supported."); } - GL.BindBuffer(BufferTarget.PixelPackBuffer, range.Handle.ToInt32()); + GL.BindBuffer(BufferTarget.PixelPackBuffer, range.Handle); FormatInfo format = FormatTable.GetFormatInfo(Info.Format); if (format.PixelFormat == PixelFormat.DepthStencil) diff --git a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs index 28ebe88a5..d5c02f4df 100644 --- a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs +++ b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.OpenGL public void Map(BufferHandle handle, int size) { - GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); _maps[handle] = ptr; @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.OpenGL { if (_maps.ContainsKey(handle)) { - GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); GL.UnmapBuffer(BufferTarget.CopyWriteBuffer); _maps.Remove(handle); @@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.OpenGL { EnsureBuffer(size); - GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer.ToInt32()); + GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer); GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle); GL.CopyBufferSubData(BufferTarget.CopyReadBuffer, BufferTarget.CopyWriteBuffer, (nint)offset, nint.Zero, size); diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index 36db655ad..c8ca02140 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -587,7 +587,7 @@ namespace Ryujinx.Graphics.OpenGL _vertexArray.SetRangeOfIndexBuffer(); - GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); + GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle); GL.DrawElementsIndirect(_primitiveType, _elementsType, (nint)indirectBuffer.Offset); @@ -608,8 +608,8 @@ namespace Ryujinx.Graphics.OpenGL _vertexArray.SetRangeOfIndexBuffer(); - GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); - GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32()); + GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle); + GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle); GL.MultiDrawElementsIndirectCount( _primitiveType, @@ -634,7 +634,7 @@ namespace Ryujinx.Graphics.OpenGL PreDrawVbUnbounded(); - GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); + GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle); GL.DrawArraysIndirect(_primitiveType, (nint)indirectBuffer.Offset); @@ -651,8 +651,8 @@ namespace Ryujinx.Graphics.OpenGL PreDrawVbUnbounded(); - GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); - GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32()); + GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle); + GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle); GL.MultiDrawArraysIndirectCount( _primitiveType, @@ -812,10 +812,10 @@ namespace Ryujinx.Graphics.OpenGL EnsureFramebuffer(); _framebuffer.SetDualSourceBlend( - blend.ColorSrcFactor.IsDualSource() || - blend.ColorDstFactor.IsDualSource() || - blend.AlphaSrcFactor.IsDualSource() || - blend.AlphaDstFactor.IsDualSource()); + blend.ColorSrcFactor.IsDualSource || + blend.ColorDstFactor.IsDualSource || + blend.AlphaSrcFactor.IsDualSource || + blend.AlphaDstFactor.IsDualSource); if (_blendConstant != blend.BlendConstant) { @@ -1178,7 +1178,7 @@ namespace Ryujinx.Graphics.OpenGL if (color != null) { - int isBgra = color.Format.IsBgr() ? 1 : 0; + int isBgra = color.Format.IsBgr ? 1 : 0; if (_fpIsBgra[index].X != isBgra) { @@ -1349,7 +1349,7 @@ namespace Ryujinx.Graphics.OpenGL Buffer.Resize(_tfbs[i], buffer.Size); Buffer.Copy(buffer.Handle, _tfbs[i], buffer.Offset, 0, buffer.Size); - GL.BindBufferBase(BufferRangeTarget.TransformFeedbackBuffer, i, _tfbs[i].ToInt32()); + GL.BindBufferBase(BufferRangeTarget.TransformFeedbackBuffer, i, _tfbs[i]); } if (_tfEnabled) @@ -1454,7 +1454,7 @@ namespace Ryujinx.Graphics.OpenGL continue; } - GL.BindBufferRange(target, assignment.Binding, buffer.Handle.ToInt32(), (nint)buffer.Offset, buffer.Size); + GL.BindBufferRange(target, assignment.Binding, buffer.Handle, (nint)buffer.Offset, buffer.Size); } } diff --git a/src/Ryujinx.Graphics.OpenGL/VertexArray.cs b/src/Ryujinx.Graphics.OpenGL/VertexArray.cs index 2480b6af2..f56a37c68 100644 --- a/src/Ryujinx.Graphics.OpenGL/VertexArray.cs +++ b/src/Ryujinx.Graphics.OpenGL/VertexArray.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.OpenGL minVertexCount = vertexCount; } - GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle.ToInt32(), (nint)vb.Buffer.Offset, vb.Stride); + GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle, (nint)vb.Buffer.Offset, vb.Stride); GL.VertexBindingDivisor(bindingIndex, vb.Divisor); _vertexBuffersInUse |= 1u << bindingIndex; } @@ -134,19 +134,19 @@ namespace Ryujinx.Graphics.OpenGL public void SetIndexBuffer(BufferRange range) { _indexBuffer = range; - GL.BindBuffer(BufferTarget.ElementArrayBuffer, range.Handle.ToInt32()); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, range.Handle); } public void SetRangeOfIndexBuffer() { Buffer.Resize(_tempIndexBuffer, _indexBuffer.Size); Buffer.Copy(_indexBuffer.Handle, _tempIndexBuffer, _indexBuffer.Offset, 0, _indexBuffer.Size); - GL.BindBuffer(BufferTarget.ElementArrayBuffer, _tempIndexBuffer.ToInt32()); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, _tempIndexBuffer); } public void RestoreIndexBuffer() { - GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer.Handle.ToInt32()); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer.Handle); } public void PreDraw(int vertexCount) @@ -188,7 +188,7 @@ namespace Ryujinx.Graphics.OpenGL Buffer.Copy(vb.Buffer.Handle, tempVertexBuffer, vb.Buffer.Offset, currentTempVbOffset, vb.Buffer.Size); Buffer.Clear(tempVertexBuffer, currentTempVbOffset + vb.Buffer.Size, requiredSize - vb.Buffer.Size, 0); - GL.BindVertexBuffer(vbIndex, tempVertexBuffer.ToInt32(), (nint)currentTempVbOffset, vb.Stride); + GL.BindVertexBuffer(vbIndex, tempVertexBuffer, (nint)currentTempVbOffset, vb.Stride); currentTempVbOffset += requiredSize; _vertexBuffersLimited |= 1u << vbIndex; @@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.OpenGL ref VertexBufferDescriptor vb = ref _vertexBuffers[vbIndex]; - GL.BindVertexBuffer(vbIndex, vb.Buffer.Handle.ToInt32(), (nint)vb.Buffer.Offset, vb.Stride); + GL.BindVertexBuffer(vbIndex, vb.Buffer.Handle, (nint)vb.Buffer.Offset, vb.Stride); buffersLimited &= ~(1u << vbIndex); } diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index bb4df0c06..fb3208f00 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -70,7 +70,7 @@ namespace Ryujinx.Graphics.OpenGL GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); - TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view; + TextureView viewConverted = view.Format.IsBgr ? _renderer.TextureCopy.BgraSwap(view) : view; UpdateEffect(); @@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.OpenGL viewConverted = _antiAliasing.Run(viewConverted, _width, _height); - if (viewConverted.Format.IsBgr()) + if (viewConverted.Format.IsBgr) { TextureView swappedView = _renderer.TextureCopy.BgraSwap(viewConverted); @@ -152,14 +152,14 @@ namespace Ryujinx.Graphics.OpenGL if (ScreenCaptureRequested) { - CaptureFrame(srcX0, srcY0, srcX1, srcY1, view.Format.IsBgr(), crop.FlipX, crop.FlipY); + CaptureFrame(srcX0, srcY0, srcX1, srcY1, view.Format.IsBgr, crop.FlipX, crop.FlipY); ScreenCaptureRequested = false; } if (_scalingFilter != null) { - if (viewConverted.Format.IsBgr() && !_isBgra) + if (viewConverted.Format.IsBgr && !_isBgra) { RecreateUpscalingTexture(true); } diff --git a/src/Ryujinx.Graphics.Shader/AttributeType.cs b/src/Ryujinx.Graphics.Shader/AttributeType.cs index 4c2913416..524453287 100644 --- a/src/Ryujinx.Graphics.Shader/AttributeType.cs +++ b/src/Ryujinx.Graphics.Shader/AttributeType.cs @@ -20,20 +20,18 @@ namespace Ryujinx.Graphics.Shader static class AttributeTypeExtensions { - public static AggregateType ToAggregateType(this AttributeType type) + extension(AttributeType type) { - return (type & ~AttributeType.AnyPacked) switch + public AggregateType Aggregate => + (type & ~AttributeType.AnyPacked) switch { AttributeType.Float => AggregateType.FP32, AttributeType.Sint => AggregateType.S32, AttributeType.Uint => AggregateType.U32, _ => throw new ArgumentException($"Invalid attribute type \"{type}\"."), }; - } - - public static AggregateType ToAggregateType(this AttributeType type, bool supportsScaledFormats) - { - return (type & ~AttributeType.AnyPacked) switch + + public AggregateType AsAggregate(bool supportsScaledFormats) => (type & ~AttributeType.AnyPacked) switch { AttributeType.Float => AggregateType.FP32, AttributeType.Sint => AggregateType.S32, diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index e0d7cdc4b..a7b82e742 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { if (context.Definitions.Stage == ShaderStage.Geometry) { - string inPrimitive = context.Definitions.InputTopology.ToGlslString(); + string inPrimitive = context.Definitions.InputTopology.GlslString; context.AppendLine($"layout (invocations = {context.Definitions.ThreadsPerInputPrimitive}, {inPrimitive}) in;"); @@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - string outPrimitive = context.Definitions.OutputTopology.ToGlslString(); + string outPrimitive = context.Definitions.OutputTopology.GlslString; int maxOutputVertices = context.Definitions.MaxOutputVertices; context.AppendLine($"layout ({outPrimitive}, max_vertices = {maxOutputVertices}) out;"); @@ -123,8 +123,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl tessCw = !tessCw; } - string patchType = context.Definitions.TessPatchType.ToGlsl(); - string spacing = context.Definitions.TessSpacing.ToGlsl(); + string patchType = context.Definitions.TessPatchType.Glsl; + string spacing = context.Definitions.TessSpacing.Glsl; string windingOrder = tessCw ? "cw" : "ccw"; context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;"); @@ -351,7 +351,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl arrayDecl = "[]"; } - string samplerTypeName = definition.Separate ? definition.Type.ToGlslTextureType() : definition.Type.ToGlslSamplerType(); + string samplerTypeName = definition.Separate ? definition.Type.GlslTextureTypeName : definition.Type.GlslSamplerTypeName; string layout = string.Empty; @@ -379,7 +379,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl arrayDecl = "[]"; } - string imageTypeName = definition.Type.ToGlslImageType(definition.Format.GetComponentType()); + string imageTypeName = definition.Type.GetGlslImageTypeName(definition.Format.GetComponentType()); if (definition.Flags.HasFlag(TextureUsageFlags.ImageCoherent)) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 32c930557..881ca0d47 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCallBuilder.Append('('); texCallBuilder.Append(imageName); - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int pCount = coordsCount + (isArray ? 1 : 0); @@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { AstTextureOperation texOp = (AstTextureOperation)operation; - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int coordsIndex = 0; string samplerName = GetSamplerName(context, texOp, ref coordsIndex); @@ -264,7 +264,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCall += "(" + samplerName; - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int pCount = coordsCount; @@ -658,7 +658,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions samplerName = $"{samplerName}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; } - name = $"{texOp.Type.ToGlslSamplerType()}({name}, {samplerName})"; + name = $"{texOp.Type.GlslSamplerTypeName}({name}, {samplerName})"; } return name; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index c0a597a10..e1571fc78 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -385,12 +385,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static void DeclarePerVertexBlock(CodeGenContext context) { - if (context.Definitions.Stage.IsVtg()) + if (context.Definitions.Stage.IsVtg) { if (context.Definitions.Stage != ShaderStage.Vertex) { SpvInstruction perVertexInputStructType = CreatePerVertexStructType(context); - int arraySize = context.Definitions.Stage == ShaderStage.Geometry ? context.Definitions.InputTopology.ToInputVertices() : 32; + int arraySize = context.Definitions.Stage == ShaderStage.Geometry ? context.Definitions.InputTopology.InputVertexCount : 32; SpvInstruction perVertexInputArrayType = context.TypeArray(perVertexInputStructType, context.Constant(context.TypeU32(), arraySize)); SpvInstruction perVertexInputPointerType = context.TypePointer(StorageClass.Input, perVertexInputArrayType); SpvInstruction perVertexInputVariable = context.Variable(perVertexInputPointerType, StorageClass.Input); @@ -537,7 +537,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Definitions.Stage, isOutput)) { - int arraySize = context.Definitions.Stage == ShaderStage.Geometry ? context.Definitions.InputTopology.ToInputVertices() : 32; + int arraySize = context.Definitions.Stage == ShaderStage.Geometry ? context.Definitions.InputTopology.InputVertexCount : 32; spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), arraySize)); if (context.Definitions.GpPassthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 27b5c21c0..83b037c1c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -615,7 +615,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv image = context.AccessChain(imagePointerType, image, textureIndex); } - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int pCount = coordsCount + (isArray ? 1 : 0); @@ -693,7 +693,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv image = context.Load(declaration.ImageType, image); - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int pCount = coordsCount + (isArray ? 1 : 0); @@ -750,7 +750,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv image = context.Load(declaration.ImageType, image); - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int pCount = coordsCount + (isArray ? 1 : 0); @@ -840,7 +840,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()]; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); - int pCount = texOp.Type.GetDimensions(); + int pCount = texOp.Type.Dimensions; SpvInstruction pCoords; @@ -1164,7 +1164,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()]; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex); - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int pCount = coordsCount; @@ -1463,7 +1463,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SamplerType type = context.SamplersTypes[texOp.GetTextureSetAndBinding()]; bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer; - int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions(); + int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.Dimensions; if (type.HasFlag(SamplerType.Array)) { @@ -1486,7 +1486,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (dimensions != 1) { - result = context.CompositeExtract(context.TypeS32(), result, (SpvLiteralInteger)texOp.Index); + result = context.CompositeExtract(context.TypeS32(), result, texOp.Index); } return new OperationResult(AggregateType.S32, result); diff --git a/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index ac1f24218..45250a463 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -457,7 +457,7 @@ namespace Ryujinx.Graphics.Shader.Decoders case AttributeConsts.ClipDistance5: case AttributeConsts.ClipDistance6: case AttributeConsts.ClipDistance7: - if (definitions.Stage.IsVtg()) + if (definitions.Stage.IsVtg) { context.SetClipDistanceWritten((attr - AttributeConsts.ClipDistance0) / 4); } diff --git a/src/Ryujinx.Graphics.Shader/InputTopology.cs b/src/Ryujinx.Graphics.Shader/InputTopology.cs index 9438263de..074386b5d 100644 --- a/src/Ryujinx.Graphics.Shader/InputTopology.cs +++ b/src/Ryujinx.Graphics.Shader/InputTopology.cs @@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.Shader static class InputTopologyExtensions { - public static string ToGlslString(this InputTopology topology) + extension(InputTopology topology) { - return topology switch + public string GlslString => topology switch { InputTopology.Points => "points", InputTopology.Lines => "lines", @@ -22,11 +22,8 @@ namespace Ryujinx.Graphics.Shader InputTopology.TrianglesAdjacency => "triangles_adjacency", _ => "points", }; - } - - public static int ToInputVertices(this InputTopology topology) - { - return topology switch + + public int InputVertexCount => topology switch { InputTopology.Points => 1, InputTopology.Lines => 2, @@ -35,17 +32,14 @@ namespace Ryujinx.Graphics.Shader InputTopology.TrianglesAdjacency => 6, _ => 1, }; - } - - public static int ToInputVerticesNoAdjacency(this InputTopology topology) - { - return topology switch + + public int InputVertexCountNoAdjacency => topology switch { InputTopology.Points => 1, InputTopology.Lines or - InputTopology.LinesAdjacency => 2, + InputTopology.LinesAdjacency => 2, InputTopology.Triangles or - InputTopology.TrianglesAdjacency => 3, + InputTopology.TrianglesAdjacency => 3, _ => 1, }; } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index df84c38f1..4c7127274 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -105,7 +105,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - src = Const(context.TranslatorContext.Definitions.InputTopology.ToInputVertices() << 16); + src = Const(context.TranslatorContext.Definitions.InputTopology.InputVertexCount << 16); } } else diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index e9f930179..2f86f044d 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -228,7 +228,7 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(context.Copy(GetSrcReg(context, srcC))); } - int coordsCount = type.GetDimensions(); + int coordsCount = type.Dimensions; for (int index = 0; index < coordsCount; index++) { @@ -335,7 +335,7 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(context.Copy(Register(srcC, RegisterType.Gpr))); } - int coordsCount = type.GetDimensions(); + int coordsCount = type.Dimensions; for (int index = 0; index < coordsCount; index++) { @@ -507,7 +507,7 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(context.Copy(GetSrcReg(context, srcC))); } - int coordsCount = type.GetDimensions(); + int coordsCount = type.Dimensions; for (int index = 0; index < coordsCount; index++) { @@ -612,7 +612,7 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(context.Copy(Register(srcC, RegisterType.Gpr))); } - int coordsCount = type.GetDimensions(); + int coordsCount = type.Dimensions; for (int index = 0; index < coordsCount; index++) { diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 19b22e03b..fa0b87317 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -227,7 +227,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } } - int coordsCount = type.GetDimensions(); + int coordsCount = type.Dimensions; for (int index = 0; index < coordsCount; index++) { @@ -558,7 +558,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if ((flags & TextureFlags.Offset) != 0) { - AddTextureOffset(type.GetDimensions(), 4, 4); + AddTextureOffset(type.Dimensions, 4, 4); } } else if (texsType == TexsType.Tld4s) @@ -583,7 +583,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (tld4sOp.Aoffi) { - AddTextureOffset(type.GetDimensions(), 8, 6); + AddTextureOffset(type.Dimensions, 8, 6); flags |= TextureFlags.Offset; } @@ -714,7 +714,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - int coordsCount = type.GetDimensions(); + int coordsCount = type.Dimensions; for (int index = 0; index < coordsCount; index++) { @@ -847,7 +847,7 @@ namespace Ryujinx.Graphics.Shader.Instructions SamplerType type = ConvertSamplerType(dimensions); - int coordsCount = type.GetDimensions(); + int coordsCount = type.Dimensions; bool isArray = dimensions is TexDim.Array1d or @@ -942,26 +942,6 @@ namespace Ryujinx.Graphics.Shader.Instructions return; } - Operand Ra() - { - if (srcA > RegisterConsts.RegisterZeroIndex) - { - return Const(0); - } - - return context.Copy(Register(srcA++, RegisterType.Gpr)); - } - - Operand Rb() - { - if (srcB > RegisterConsts.RegisterZeroIndex) - { - return Const(0); - } - - return context.Copy(Register(srcB++, RegisterType.Gpr)); - } - TextureFlags flags = TextureFlags.Derivatives; List sourcesList = []; @@ -975,7 +955,7 @@ namespace Ryujinx.Graphics.Shader.Instructions SamplerType type = ConvertSamplerType(dimensions); - int coordsCount = type.GetDimensions(); + int coordsCount = type.Dimensions; for (int index = 0; index < coordsCount; index++) { @@ -1051,6 +1031,28 @@ namespace Ryujinx.Graphics.Shader.Instructions } EmitTextureSample(context, type, flags, imm, componentMask, dests, sources); + + return; + + Operand Ra() + { + if (srcA > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(srcA++, RegisterType.Gpr)); + } + + Operand Rb() + { + if (srcB > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(srcB++, RegisterType.Gpr)); + } } private static void EmitTxq( diff --git a/src/Ryujinx.Graphics.Shader/OutputTopology.cs b/src/Ryujinx.Graphics.Shader/OutputTopology.cs index dc4b304ad..a517fe861 100644 --- a/src/Ryujinx.Graphics.Shader/OutputTopology.cs +++ b/src/Ryujinx.Graphics.Shader/OutputTopology.cs @@ -9,9 +9,10 @@ namespace Ryujinx.Graphics.Shader static class OutputTopologyExtensions { - public static string ToGlslString(this OutputTopology topology) + + extension(OutputTopology topology) { - return topology switch + public string GlslString => topology switch { OutputTopology.LineStrip => "line_strip", OutputTopology.PointList => "points", diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index a693495fa..88449ef66 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -22,9 +22,9 @@ namespace Ryujinx.Graphics.Shader static class SamplerTypeExtensions { - public static int GetDimensions(this SamplerType type) + extension(SamplerType type) { - return (type & SamplerType.Mask) switch + public int Dimensions => (type & SamplerType.Mask) switch { SamplerType.Texture1D => 1, SamplerType.TextureBuffer => 1, @@ -33,127 +33,136 @@ namespace Ryujinx.Graphics.Shader SamplerType.TextureCube => 3, _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), }; - } - public static string ToShortSamplerType(this SamplerType type) - { - string typeName = (type & SamplerType.Mask) switch + public string ShortTypeName { - SamplerType.Texture1D => "1d", - SamplerType.TextureBuffer => "b", - SamplerType.Texture2D => "2d", - SamplerType.Texture3D => "3d", - SamplerType.TextureCube => "cube", - _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), - }; + get + { + string typeName = (type & SamplerType.Mask) switch + { + SamplerType.Texture1D => "1d", + SamplerType.TextureBuffer => "b", + SamplerType.Texture2D => "2d", + SamplerType.Texture3D => "3d", + SamplerType.TextureCube => "cube", + _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), + }; - if ((type & SamplerType.Multisample) != 0) - { - typeName += "ms"; + if ((type & SamplerType.Multisample) != 0) + { + typeName += "ms"; + } + + if ((type & SamplerType.Array) != 0) + { + typeName += "a"; + } + + if ((type & SamplerType.Shadow) != 0) + { + typeName += "s"; + } + + return typeName; + } } - if ((type & SamplerType.Array) != 0) + public string GlslSamplerTypeName { - typeName += "a"; + get + { + string typeName = (type & SamplerType.Mask) switch + { + SamplerType.None => "sampler", + SamplerType.Texture1D => "sampler1D", + SamplerType.TextureBuffer => "samplerBuffer", + SamplerType.Texture2D => "sampler2D", + SamplerType.Texture3D => "sampler3D", + SamplerType.TextureCube => "samplerCube", + _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), + }; + + if ((type & SamplerType.Multisample) != 0) + { + typeName += "MS"; + } + + if ((type & SamplerType.Array) != 0) + { + typeName += "Array"; + } + + if ((type & SamplerType.Shadow) != 0) + { + typeName += "Shadow"; + } + + return typeName; + } } - if ((type & SamplerType.Shadow) != 0) + public string GlslTextureTypeName { - typeName += "s"; + get + { + string typeName = (type & SamplerType.Mask) switch + { + SamplerType.Texture1D => "texture1D", + SamplerType.TextureBuffer => "textureBuffer", + SamplerType.Texture2D => "texture2D", + SamplerType.Texture3D => "texture3D", + SamplerType.TextureCube => "textureCube", + _ => throw new ArgumentException($"Invalid texture type \"{type}\"."), + }; + + if ((type & SamplerType.Multisample) != 0) + { + typeName += "MS"; + } + + if ((type & SamplerType.Array) != 0) + { + typeName += "Array"; + } + + return typeName; + } } - return typeName; - } - - public static string ToGlslSamplerType(this SamplerType type) - { - string typeName = (type & SamplerType.Mask) switch + public string GetGlslImageTypeName(AggregateType componentType) { - SamplerType.None => "sampler", - SamplerType.Texture1D => "sampler1D", - SamplerType.TextureBuffer => "samplerBuffer", - SamplerType.Texture2D => "sampler2D", - SamplerType.Texture3D => "sampler3D", - SamplerType.TextureCube => "samplerCube", - _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), - }; + string typeName = (type & SamplerType.Mask) switch + { + SamplerType.Texture1D => "image1D", + SamplerType.TextureBuffer => "imageBuffer", + SamplerType.Texture2D => "image2D", + SamplerType.Texture3D => "image3D", + SamplerType.TextureCube => "imageCube", + _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), + }; - if ((type & SamplerType.Multisample) != 0) - { - typeName += "MS"; + if ((type & SamplerType.Multisample) != 0) + { + typeName += "MS"; + } + + if ((type & SamplerType.Array) != 0) + { + typeName += "Array"; + } + + switch (componentType) + { + case AggregateType.U32: + typeName = 'u' + typeName; + break; + case AggregateType.S32: + typeName = 'i' + typeName; + break; + } + + return typeName; } - - if ((type & SamplerType.Array) != 0) - { - typeName += "Array"; - } - - if ((type & SamplerType.Shadow) != 0) - { - typeName += "Shadow"; - } - - return typeName; - } - - public static string ToGlslTextureType(this SamplerType type) - { - string typeName = (type & SamplerType.Mask) switch - { - SamplerType.Texture1D => "texture1D", - SamplerType.TextureBuffer => "textureBuffer", - SamplerType.Texture2D => "texture2D", - SamplerType.Texture3D => "texture3D", - SamplerType.TextureCube => "textureCube", - _ => throw new ArgumentException($"Invalid texture type \"{type}\"."), - }; - - if ((type & SamplerType.Multisample) != 0) - { - typeName += "MS"; - } - - if ((type & SamplerType.Array) != 0) - { - typeName += "Array"; - } - - return typeName; - } - - public static string ToGlslImageType(this SamplerType type, AggregateType componentType) - { - string typeName = (type & SamplerType.Mask) switch - { - SamplerType.Texture1D => "image1D", - SamplerType.TextureBuffer => "imageBuffer", - SamplerType.Texture2D => "image2D", - SamplerType.Texture3D => "image3D", - SamplerType.TextureCube => "imageCube", - _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), - }; - - if ((type & SamplerType.Multisample) != 0) - { - typeName += "MS"; - } - - if ((type & SamplerType.Array) != 0) - { - typeName += "Array"; - } - - switch (componentType) - { - case AggregateType.U32: - typeName = 'u' + typeName; - break; - case AggregateType.S32: - typeName = 'i' + typeName; - break; - } - - return typeName; } } } diff --git a/src/Ryujinx.Graphics.Shader/ShaderStage.cs b/src/Ryujinx.Graphics.Shader/ShaderStage.cs index faea5c357..cbfd1aa0c 100644 --- a/src/Ryujinx.Graphics.Shader/ShaderStage.cs +++ b/src/Ryujinx.Graphics.Shader/ShaderStage.cs @@ -14,27 +14,23 @@ namespace Ryujinx.Graphics.Shader public static class ShaderStageExtensions { - /// - /// Checks if the shader stage supports render scale. - /// - /// Shader stage - /// True if the shader stage supports render scale, false otherwise - public static bool SupportsRenderScale(this ShaderStage stage) + extension(ShaderStage shaderStage) { - return stage is ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute; - } - - /// - /// Checks if the shader stage is vertex, tessellation or geometry. - /// - /// Shader stage - /// True if the shader stage is vertex, tessellation or geometry, false otherwise - public static bool IsVtg(this ShaderStage stage) - { - return stage is ShaderStage.Vertex or - ShaderStage.TessellationControl or - ShaderStage.TessellationEvaluation or - ShaderStage.Geometry; + /// + /// Checks if the shader stage supports render scale. + /// + public bool SupportsRenderScale => + shaderStage is ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute; + + /// + /// Checks if the shader stage is vertex, tessellation or geometry. + /// + public bool IsVtg => + shaderStage is ShaderStage.Vertex or + ShaderStage.TessellationControl or + ShaderStage.TessellationEvaluation or + ShaderStage.Geometry; } + } } diff --git a/src/Ryujinx.Graphics.Shader/TessPatchType.cs b/src/Ryujinx.Graphics.Shader/TessPatchType.cs index 76be22fd4..6c4fc1a30 100644 --- a/src/Ryujinx.Graphics.Shader/TessPatchType.cs +++ b/src/Ryujinx.Graphics.Shader/TessPatchType.cs @@ -9,9 +9,9 @@ namespace Ryujinx.Graphics.Shader static class TessPatchTypeExtensions { - public static string ToGlsl(this TessPatchType type) + extension(TessPatchType patchType) { - return type switch + public string Glsl => patchType switch { TessPatchType.Isolines => "isolines", TessPatchType.Quads => "quads", diff --git a/src/Ryujinx.Graphics.Shader/TessSpacing.cs b/src/Ryujinx.Graphics.Shader/TessSpacing.cs index 6035366c1..8d42ccbf1 100644 --- a/src/Ryujinx.Graphics.Shader/TessSpacing.cs +++ b/src/Ryujinx.Graphics.Shader/TessSpacing.cs @@ -9,9 +9,9 @@ namespace Ryujinx.Graphics.Shader static class TessSpacingExtensions { - public static string ToGlsl(this TessSpacing spacing) + extension(TessSpacing spacing) { - return spacing switch + public string Glsl => spacing switch { TessSpacing.FractionalEventSpacing => "fractional_even_spacing", TessSpacing.FractionalOddSpacing => "fractional_odd_spacing", diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 94448626f..62dd9e2e7 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -135,7 +135,7 @@ namespace Ryujinx.Graphics.Shader.Translation } else if (TranslatorContext.Stage == ShaderStage.Geometry) { - int inputVertices = TranslatorContext.Definitions.InputTopology.ToInputVertices(); + int inputVertices = TranslatorContext.Definitions.InputTopology.InputVertexCount; Operand baseVertex = this.IMultiply(outputVertexOffset, Const(inputVertices)); @@ -404,7 +404,7 @@ namespace Ryujinx.Graphics.Shader.Translation else { inputStart = 0; - inputEnd = topology.ToInputVerticesNoAdjacency(); + inputEnd = topology.InputVertexCountNoAdjacency; inputStep = 1; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index 1f2f79a2d..c40568a61 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -39,8 +39,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // Set any destination variables to zero. string typeName = texOp.Inst.IsImage() - ? texOp.Type.ToGlslImageType(texOp.Format.GetComponentType()) - : texOp.Type.ToGlslTextureType(); + ? texOp.Type.GetGlslImageTypeName(texOp.Format.GetComponentType()) + : texOp.Type.GlslTextureTypeName; gpuAccessor.Log($"Failed to find handle source for bindless access of type \"{typeName}\"."); diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 2d366be71..2fc15344e 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Shader.Translation } else if (stage == ShaderStage.Geometry) { - LocalTopologyRemapMemoryId = AddMemoryDefinition("local_topology_remap", AggregateType.Array | AggregateType.U32, inputTopology.ToInputVertices()); + LocalTopologyRemapMemoryId = AddMemoryDefinition("local_topology_remap", AggregateType.Array | AggregateType.U32, inputTopology.InputVertexCount); LocalGeometryOutputVertexCountMemoryId = AddMemoryDefinition("local_geometry_output_vertex", AggregateType.U32); LocalGeometryOutputIndexCountMemoryId = AddMemoryDefinition("local_geometry_output_index", AggregateType.U32); @@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool coherent, bool separate) { - int dimensions = type == SamplerType.None ? 0 : type.GetDimensions(); + int dimensions = type == SamplerType.None ? 0 : type.Dimensions; Dictionary dict = isImage ? _usedImages : _usedTextures; TextureUsageFlags usageFlags = TextureUsageFlags.None; @@ -282,7 +282,7 @@ namespace Ryujinx.Graphics.Shader.Translation { usageFlags |= TextureUsageFlags.NeedsScaleValue; - bool canScale = _stage.SupportsRenderScale() && arrayLength == 1 && !write && dimensions == 2; + bool canScale = _stage.SupportsRenderScale && arrayLength == 1 && !write && dimensions == 2; if (!canScale) { @@ -355,7 +355,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (arrayLength != 1 && type != SamplerType.None) { - prefix += type.ToShortSamplerType(); + prefix += type.ShortTypeName; } if (isImage) @@ -432,9 +432,9 @@ namespace Ryujinx.Graphics.Shader.Translation if (found) { selectedMeta.UsageFlags |= TextureUsageFlags.NeedsScaleValue; - - int dimensions = type.GetDimensions(); - bool canScale = _stage.SupportsRenderScale() && selectedInfo.ArrayLength == 1 && dimensions == 2; + + int dimensions = type.Dimensions; + bool canScale = _stage.SupportsRenderScale && selectedInfo.ArrayLength == 1 && dimensions == 2; if (!canScale) { diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs index 656ad6c5e..c11e65812 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs @@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Shader.Translation GpPassthrough = gpPassthrough; ThreadsPerInputPrimitive = threadsPerInputPrimitive; OutputTopology = outputTopology; - MaxOutputVertices = gpPassthrough ? graphicsState.Topology.ToInputVerticesNoAdjacency() : maxOutputVertices; + MaxOutputVertices = gpPassthrough ? graphicsState.Topology.InputVertexCountNoAdjacency : maxOutputVertices; ImapTypes = imapTypes; OmapTargets = omapTargets; OmapSampleMask = omapSampleMask; @@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Shader.Translation public AggregateType GetFragmentOutputColorType(int location) { - return AggregateType.Vector4 | _graphicsState.FragmentOutputTypes[location].ToAggregateType(); + return AggregateType.Vector4 | _graphicsState.FragmentOutputTypes[location].Aggregate; } public AggregateType GetUserDefinedType(int location, bool isOutput) @@ -307,7 +307,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (Stage == ShaderStage.Vertex && !isOutput) { - type |= _graphicsState.AttributeTypes[location].ToAggregateType(SupportsScaledVertexFormats); + type |= _graphicsState.AttributeTypes[location].AsAggregate(SupportsScaledVertexFormats); } else { diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs index 808692559..2ec00ce2d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms (intCoords || isImage) && !isBindless && !isIndexed && - stage.SupportsRenderScale() && + stage.SupportsRenderScale && TypeSupportsScale(texOp.Type)) { int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale); @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms ? resourceManager.GetTextureDescriptors(includeArrays: false).Length + resourceManager.FindImageDescriptorIndex(texOp.Binding) : resourceManager.FindTextureDescriptorIndex(texOp.Binding); - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int coordsIndex = isBindless ? 1 : 0; for (int index = 0; index < coordsCount; index++) @@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Index < 2 && !isBindless && !isIndexed && - stage.SupportsRenderScale() && + stage.SupportsRenderScale && TypeSupportsScale(texOp.Type)) { int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale); @@ -168,7 +168,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms return node; } - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; @@ -226,7 +226,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms bool isIndexed = resourceManager.IsArrayOfTexturesOrImages(texOp.Binding, isImage: false); - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int coordsIndex = isBindless || isIndexed ? 1 : 0; int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; @@ -315,7 +315,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0; bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; - int coordsCount = texOp.Type.GetDimensions(); + int coordsCount = texOp.Type.Dimensions; int offsetsCount; diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 38cd02449..ca0e79b8b 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -368,10 +368,13 @@ namespace Ryujinx.Graphics.Vulkan } } - public BufferHandle GetHandle() + public BufferHandle Handle { - ulong handle = _bufferHandle; - return Unsafe.As(ref handle); + get + { + ulong handle = _bufferHandle; + return Unsafe.As(ref handle); + } } public nint Map(int offset, int mappingSize) diff --git a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index aee55fef4..605105ea8 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -158,16 +158,16 @@ namespace Ryujinx.Graphics.Vulkan FormatFeatureFlags.TransferSrcBit | FormatFeatureFlags.TransferDstBit; - if (srcFormat.IsDepthOrStencil()) + if (srcFormat.IsDepthOrStencil) { requiredFeatures |= FormatFeatureFlags.DepthStencilAttachmentBit; } - else if (srcFormat.IsRtColorCompatible()) + else if (srcFormat.IsRtColorCompatible) { requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit; } - if (srcFormat.IsImageCompatible() && storageFeatureFlagRequired) + if (srcFormat.IsImageCompatible && storageFeatureFlagRequired) { requiredFeatures |= FormatFeatureFlags.StorageImageBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 46b9cacb9..919c45b9d 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Vulkan { Format format = view.Info.Format; - bool isDepthStencil = format.IsDepthOrStencil(); + bool isDepthStencil = format.IsDepthOrStencil; _device = device; _attachments = [view.GetImageViewForAttachment()]; @@ -62,8 +62,8 @@ namespace Ryujinx.Graphics.Vulkan AttachmentSamples = [(uint)view.Info.Samples]; AttachmentFormats = [view.VkFormat]; AttachmentIndices = isDepthStencil ? [] : [0]; - AttachmentIntegerFormatMask = format.IsInteger() ? 1u : 0u; - LogicOpsAllowed = !format.IsFloatOrSrgb(); + AttachmentIntegerFormatMask = format.IsInt ? 1u : 0u; + LogicOpsAllowed = !format.IsFloatOrSrgb; AttachmentsCount = 1; _totalCount = 1; @@ -113,12 +113,12 @@ namespace Ryujinx.Graphics.Vulkan Format format = texture.Info.Format; - if (format.IsInteger()) + if (format.IsInt) { attachmentIntegerFormatMask |= 1u << bindIndex; } - allFormatsFloatOrSrgb &= format.IsFloatOrSrgb(); + allFormatsFloatOrSrgb &= format.IsFloatOrSrgb; width = Math.Min(width, (uint)texture.Width); height = Math.Min(height, (uint)texture.Height); @@ -250,12 +250,12 @@ namespace Ryujinx.Graphics.Vulkan Format format = texture.Info.Format; - if (format.IsInteger()) + if (format.IsInt) { attachmentIntegerFormatMask |= 1u << bindIndex; } - allFormatsFloatOrSrgb &= format.IsFloatOrSrgb(); + allFormatsFloatOrSrgb &= format.IsFloatOrSrgb; width = Math.Min(width, (uint)texture.Width); height = Math.Min(height, (uint)texture.Height); @@ -330,12 +330,12 @@ namespace Ryujinx.Graphics.Vulkan { Format format = _colors[index].Info.Format; - if (format.IsSint()) + if (format.IsSignedInt) { return ComponentType.SignedInteger; } - if (format.IsUint()) + if (format.IsUnsignedInt) { return ComponentType.UnsignedInteger; } diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index a60e060c0..dbb5ee224 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -402,14 +402,14 @@ namespace Ryujinx.Graphics.Vulkan 0f, 1f); - bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil; if (dstIsDepthOrStencil) { - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); + _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programDepthBlitMs : _programDepthBlit); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); } - else if (src.Info.Target.IsMultisample()) + else if (src.Info.Target.IsMultisample) { _pipeline.SetProgram(_programColorBlitMs); } @@ -566,12 +566,12 @@ namespace Ryujinx.Graphics.Vulkan if (isDepth) { - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); + _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programDepthBlitMs : _programDepthBlit); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); } else { - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit); + _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programStencilBlitMs : _programStencilBlit); _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); } @@ -1047,7 +1047,7 @@ namespace Ryujinx.Graphics.Vulkan Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; int samples = src.Info.Samples; - bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil(); + bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil; ImageAspectFlags aspectFlags = src.Info.Format.ConvertAspectFlags(); // X and Y are the expected texture samples. @@ -1173,7 +1173,7 @@ namespace Ryujinx.Graphics.Vulkan Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; int samples = dst.Info.Samples; - bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil(); + bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil; ImageAspectFlags aspectFlags = src.Info.Format.ConvertAspectFlags(); // X and Y are the expected texture samples. diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 26682ad20..f2f68378f 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1181,7 +1181,7 @@ namespace Ryujinx.Graphics.Vulkan if (!attribute.IsZero) { - newVbScalarSizes[rawIndex] = Math.Max(newVbScalarSizes[rawIndex], attribute.Format.GetScalarSize()); + newVbScalarSizes[rawIndex] = Math.Max(newVbScalarSizes[rawIndex], attribute.Format.ScalarSize); dirtyVbSizes |= 1u << rawIndex; } @@ -1575,7 +1575,7 @@ namespace Ryujinx.Graphics.Vulkan // May need to enforce feedback loop layout here in the future. // Though technically, it should always work with the general layout. - if (view.Info.Format.IsDepthOrStencil()) + if (view.Info.Format.IsDepthOrStencil) { if (_passWritesDepthStencil) { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index c259d91a9..e0bff2c15 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Vulkan int maxColorAttachmentIndex = -1; bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample || - !state.DepthStencilFormat.IsImageCompatible(); + !state.DepthStencilFormat.IsImageCompatible; Span attachmentEnableSpan = state.AttachmentEnable.AsSpan(); Span attachmentFormatsSpan = state.AttachmentFormats.AsSpan(); @@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Vulkan if (attachmentEnableSpan[i]) { bool isNotMsOrSupportsStorageAttachments = gd.Capabilities.SupportsShaderStorageImageMultisample || - !attachmentFormatsSpan[i].IsImageCompatible(); + !attachmentFormatsSpan[i].IsImageCompatible; attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(attachmentFormatsSpan[i], isNotMsOrSupportsStorageAttachments); @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < attachmentCount; i++) { - int bindIndex = attachmentIndices[i]; + //int bindIndex = attachmentIndices[i]; attachmentDescs[i] = new AttachmentDescription( 0, @@ -242,7 +242,7 @@ namespace Ryujinx.Graphics.Vulkan if (!attribute.IsZero && bufferIndex < vbCount) { - vbScalarSizes[bufferIndex - 1] = Math.Max(attribute.Format.GetScalarSize(), vbScalarSizes[bufferIndex - 1]); + vbScalarSizes[bufferIndex - 1] = Math.Max(attribute.Format.ScalarSize, vbScalarSizes[bufferIndex - 1]); } } @@ -320,23 +320,23 @@ namespace Ryujinx.Graphics.Vulkan if (attachmentEnableSpan[i]) { bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample || - !attachmentFormatsSpan[i].IsImageCompatible(); + !attachmentFormatsSpan[i].IsImageCompatible; pAttachmentFormatsSpan[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(attachmentFormatsSpan[i], isNotMsOrSupportsStorage); maxColorAttachmentIndex = i; - if (attachmentFormatsSpan[i].IsInteger()) + if (attachmentFormatsSpan[i].IsInt) { attachmentIntegerFormatMask |= 1u << i; } - allFormatsFloatOrSrgb &= attachmentFormatsSpan[i].IsFloatOrSrgb(); + allFormatsFloatOrSrgb &= attachmentFormatsSpan[i].IsFloatOrSrgb; } } if (state.DepthStencilEnable) { - bool isNotMsOrSupportsStorage = !state.DepthStencilFormat.IsImageCompatible() || + bool isNotMsOrSupportsStorage = !state.DepthStencilFormat.IsImageCompatible || gd.Capabilities.SupportsShaderStorageImageMultisample; pAttachmentFormatsSpan[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index 36f5804bb..75fff2034 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -647,7 +647,7 @@ namespace Ryujinx.Graphics.Vulkan { result.ThrowOnError(); } - else if (result.IsError()) + else if (result.IsError) { program.AddGraphicsPipeline(ref Internal, null); diff --git a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs index e0de5692c..aae3b0afb 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan ImageBlit.SrcOffsetsBuffer srcOffsets = new(); ImageBlit.DstOffsetsBuffer dstOffsets = new(); - Filter filter = linearFilter && !dstInfo.Format.IsDepthOrStencil() ? Filter.Linear : Filter.Nearest; + Filter filter = linearFilter && !dstInfo.Format.IsDepthOrStencil ? Filter.Linear : Filter.Nearest; TextureView.InsertImageBarrier( api, diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 3dc605891..46cd5b4be 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -77,7 +77,7 @@ namespace Ryujinx.Graphics.Vulkan _device = device; _info = info; - bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample(); + bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample; VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); uint levels = (uint)info.Levels; @@ -311,16 +311,16 @@ namespace Ryujinx.Graphics.Vulkan { ImageUsageFlags usage = DefaultUsageFlags; - if (format.IsDepthOrStencil()) + if (format.IsDepthOrStencil) { usage |= ImageUsageFlags.DepthStencilAttachmentBit; } - else if (format.IsRtColorCompatible()) + else if (format.IsRtColorCompatible) { usage |= ImageUsageFlags.ColorAttachmentBit; } - if ((format.IsImageCompatible() && isMsImageStorageSupported) || extendedUsage) + if ((format.IsImageCompatible && isMsImageStorageSupported) || extendedUsage) { usage |= ImageUsageFlags.StorageBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index 1cbb7c6e1..4513c804f 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Textures.Add(this); - bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample(); + bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample; VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); ImageUsageFlags usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false); @@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Vulkan ImageUsageFlags shaderUsage = ImageUsageFlags.SampledBit; - if (info.Format.IsImageCompatible() && (_gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample())) + if (info.Format.IsImageCompatible && (_gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample)) { shaderUsage |= ImageUsageFlags.StorageBit; } @@ -150,7 +150,7 @@ namespace Ryujinx.Graphics.Vulkan { if (gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView)) { - if (levels == 1 && (info.Format.IsRtColorCompatible() || info.Format.IsDepthOrStencil())) + if (levels == 1 && (info.Format.IsRtColorCompatible || info.Format.IsDepthOrStencil)) { subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, 1); @@ -225,12 +225,12 @@ namespace Ryujinx.Graphics.Vulkan Image srcImage = src.GetImage().Get(cbs).Value; Image dstImage = dst.GetImage().Get(cbs).Value; - if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) + if (!dst.Info.Target.IsMultisample && Info.Target.IsMultisample) { int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers); } - else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) + 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); @@ -241,7 +241,7 @@ namespace Ryujinx.Graphics.Vulkan int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels); } - else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) + else if (src.Info.Format.IsDepthOrStencil != dst.Info.Format.IsDepthOrStencil) { int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); @@ -285,11 +285,11 @@ namespace Ryujinx.Graphics.Vulkan Image srcImage = src.GetImage().Get(cbs).Value; Image dstImage = dst.GetImage().Get(cbs).Value; - if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) + if (!dst.Info.Target.IsMultisample && Info.Target.IsMultisample) { _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); } - else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) + else if (dst.Info.Target.IsMultisample && !Info.Target.IsMultisample) { _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); } @@ -297,7 +297,7 @@ namespace Ryujinx.Graphics.Vulkan { _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); } - else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) + else if (src.Info.Format.IsDepthOrStencil != dst.Info.Format.IsDepthOrStencil) { _gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); } @@ -370,7 +370,7 @@ namespace Ryujinx.Graphics.Vulkan src.Height == dst.Height && src.VkFormat == dst.VkFormat) { - if (src.Info.Samples > 1 && src.Info.Samples != dst.Info.Samples && src.Info.Format.IsDepthOrStencil()) + 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. @@ -424,7 +424,7 @@ namespace Ryujinx.Graphics.Vulkan } } - bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil; if (!VulkanConfiguration.UseUnsafeBlit || (_gd.Vendor != Vendor.Nvidia && _gd.Vendor != Vendor.Intel)) { diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanException.cs b/src/Ryujinx.Graphics.Vulkan/VulkanException.cs index 5d67ab838..1ccf0363a 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanException.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanException.cs @@ -5,18 +5,17 @@ namespace Ryujinx.Graphics.Vulkan { static class ResultExtensions { - public static bool IsError(this Result result) + extension(Result result) { - // Only negative result codes are errors. - return result < Result.Success; - } + public bool IsError => result < Result.Success; - public static void ThrowOnError(this Result result) - { - // Only negative result codes are errors. - if (result.IsError()) + public void ThrowOnError() { - throw new VulkanException(result); + // Only negative result codes are errors. + if (result.IsError) + { + throw new VulkanException(result); + } } } } diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index b67b0dbfa..0a0d970c1 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -401,7 +401,7 @@ namespace Ryujinx.Graphics.Vulkan cbs = _gd.CommandBufferPool.Rent(); } - CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY); + CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr, crop.FlipX, crop.FlipY); ScreenCaptureRequested = false; } diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 517f8ef16..83aaa1f4d 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -122,8 +122,8 @@ namespace Ryujinx.HLE.HOS TickSource, device, device.Memory, - device.Configuration.MemoryConfiguration.ToKernelMemorySize(), - device.Configuration.MemoryConfiguration.ToKernelMemoryArrange()); + device.Configuration.MemoryConfiguration.KernelMemorySize, + device.Configuration.MemoryConfiguration.KernelMemoryArrange); Device = device; diff --git a/src/Ryujinx.HLE/HOS/HorizonFsClient.cs b/src/Ryujinx.HLE/HOS/HorizonFsClient.cs index 56bc3bec3..cffd89413 100644 --- a/src/Ryujinx.HLE/HOS/HorizonFsClient.cs +++ b/src/Ryujinx.HLE/HOS/HorizonFsClient.cs @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS public Result GetFileSize(out long size, FileHandle handle) { - return _fsClient.GetFileSize(out size, (LibHac.Fs.FileHandle)handle.Value).ToHorizonResult(); + return _fsClient.GetFileSize(out size, (LibHac.Fs.FileHandle)handle.Value).Horizon; } public Result MountSystemData(string mountName, ulong dataId) @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS using IFileSystem ncaFileSystem = nca.OpenFileSystem(NcaSectionType.Data, _system.FsIntegrityCheckLevel); using UniqueRef ncaFsRef = new(ncaFileSystem); - Result result = _fsClient.Register(mountName.ToU8Span(), ref ncaFsRef.Ref).ToHorizonResult(); + Result result = _fsClient.Register(mountName.ToU8Span(), ref ncaFsRef.Ref).Horizon; if (result.IsFailure) { ncaStorage.Dispose(); @@ -74,14 +74,14 @@ namespace Ryujinx.HLE.HOS { ncaStorage?.Dispose(); - return ex.ResultValue.ToHorizonResult(); + return ex.ResultValue.Horizon; } } } // TODO: Return correct result here, this is likely wrong. - return LibHac.Fs.ResultFs.TargetNotFound.Handle().ToHorizonResult(); + return LibHac.Fs.ResultFs.TargetNotFound.Handle().Horizon; } public Result OpenFile(out FileHandle handle, string path, OpenMode openMode) @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS LibHac.Result result = _fsClient.OpenFile(out LibHac.Fs.FileHandle libhacHandle, path.ToU8Span(), (LibHac.Fs.OpenMode)openMode); handle = new(libhacHandle); - return result.ToHorizonResult(); + return result.Horizon; } public Result QueryMountSystemDataCacheSize(out long size, ulong dataId) @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS public Result ReadFile(FileHandle handle, long offset, Span destination) { - return _fsClient.ReadFile((LibHac.Fs.FileHandle)handle.Value, offset, destination).ToHorizonResult(); + return _fsClient.ReadFile((LibHac.Fs.FileHandle)handle.Value, offset, destination).Horizon; } public void Unmount(string mountName) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs index dd133ee15..845ac850c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs @@ -4,19 +4,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { static class CapabilityExtensions { + extension(CapabilityType type) + { + public uint Flag => (uint)type + 1; + + public uint Id => (uint)BitOperations.TrailingZeroCount(type.Flag); + } + 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()); - } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index 745d3edd8..2d9d0ef47 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -133,7 +133,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return Result.Success; } - int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.GetFlag() + 1)); + int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.Flag + 1)); // Check if the property was already set. if (((mask0 & codeMask) & 0x1e008) != 0) diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index 0d2fcaa2a..28db75663 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -1316,8 +1316,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer); - Span deviceHandles = MemoryMarshal.Cast(vibrationDeviceHandleBuffer); - Span vibrationValues = MemoryMarshal.Cast(vibrationValueBuffer); + Span deviceHandles = MemoryMarshal.Cast(new Span(vibrationDeviceHandleBuffer)); + Span vibrationValues = MemoryMarshal.Cast(new Span(vibrationValueBuffer)); if (!deviceHandles.IsEmpty && vibrationValues.Length == deviceHandles.Length) { diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs b/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs index 301d415a0..6a9b4d442 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Spl #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(); + MemorySize memorySize = context.Device.Configuration.MemoryConfiguration.KernelMemorySize; switch (configItem) { diff --git a/src/Ryujinx.HLE/MemoryConfiguration.cs b/src/Ryujinx.HLE/MemoryConfiguration.cs index 21ecd737f..9a397098a 100644 --- a/src/Ryujinx.HLE/MemoryConfiguration.cs +++ b/src/Ryujinx.HLE/MemoryConfiguration.cs @@ -18,11 +18,11 @@ namespace Ryujinx.HLE { private const ulong GiB = 1024 * 1024 * 1024; -#pragma warning disable IDE0055 // Disable formatting - public static MemoryArrange ToKernelMemoryArrange(this MemoryConfiguration configuration) + extension(MemoryConfiguration configuration) { - return configuration switch + public MemoryArrange KernelMemoryArrange => configuration switch { +#pragma warning disable IDE0055 // Disable formatting MemoryConfiguration.MemoryConfiguration4GiB => MemoryArrange.MemoryArrange4GiB, MemoryConfiguration.MemoryConfiguration4GiBAppletDev => MemoryArrange.MemoryArrange4GiBAppletDev, MemoryConfiguration.MemoryConfiguration4GiBSystemDev => MemoryArrange.MemoryArrange4GiBSystemDev, @@ -31,38 +31,36 @@ namespace Ryujinx.HLE MemoryConfiguration.MemoryConfiguration8GiB => MemoryArrange.MemoryArrange8GiB, MemoryConfiguration.MemoryConfiguration12GiB => MemoryArrange.MemoryArrange12GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), +#pragma warning restore IDE0055 }; - } - - public static MemorySize ToKernelMemorySize(this MemoryConfiguration configuration) - { - return configuration switch + + public MemorySize KernelMemorySize => configuration switch { +#pragma warning disable IDE0055 // Disable formatting MemoryConfiguration.MemoryConfiguration4GiB or - MemoryConfiguration.MemoryConfiguration4GiBAppletDev or - MemoryConfiguration.MemoryConfiguration4GiBSystemDev => MemorySize.MemorySize4GiB, + MemoryConfiguration.MemoryConfiguration4GiBAppletDev or + MemoryConfiguration.MemoryConfiguration4GiBSystemDev => MemorySize.MemorySize4GiB, MemoryConfiguration.MemoryConfiguration6GiB or - MemoryConfiguration.MemoryConfiguration6GiBAppletDev => MemorySize.MemorySize6GiB, - MemoryConfiguration.MemoryConfiguration8GiB => MemorySize.MemorySize8GiB, - MemoryConfiguration.MemoryConfiguration12GiB => MemorySize.MemorySize12GiB, + MemoryConfiguration.MemoryConfiguration6GiBAppletDev => MemorySize.MemorySize6GiB, + MemoryConfiguration.MemoryConfiguration8GiB => MemorySize.MemorySize8GiB, + MemoryConfiguration.MemoryConfiguration12GiB => MemorySize.MemorySize12GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), +#pragma warning restore IDE0055 }; - } - - public static ulong ToDramSize(this MemoryConfiguration configuration) - { - return configuration switch + + public ulong DramSize => configuration switch { +#pragma warning disable IDE0055 // Disable formatting MemoryConfiguration.MemoryConfiguration4GiB or - MemoryConfiguration.MemoryConfiguration4GiBAppletDev or - MemoryConfiguration.MemoryConfiguration4GiBSystemDev => 4 * GiB, + MemoryConfiguration.MemoryConfiguration4GiBAppletDev or + MemoryConfiguration.MemoryConfiguration4GiBSystemDev => 4 * GiB, MemoryConfiguration.MemoryConfiguration6GiB or - MemoryConfiguration.MemoryConfiguration6GiBAppletDev => 6 * GiB, + MemoryConfiguration.MemoryConfiguration6GiBAppletDev => 6 * GiB, MemoryConfiguration.MemoryConfiguration8GiB => 8 * GiB, MemoryConfiguration.MemoryConfiguration12GiB => 12 * GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), +#pragma warning restore IDE0055 }; } -#pragma warning restore IDE0055 } } diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 2ce9d9959..850c8b5fa 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -83,7 +83,7 @@ namespace Ryujinx.HLE #pragma warning disable IDE0055 // Disable formatting DirtyHacks = new DirtyHacks(Configuration.Hacks); AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver); - Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); + Memory = new MemoryBlock(Configuration.MemoryConfiguration.DramSize, memoryAllocationFlags); Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks); Debugger = Configuration.EnableGdbStub ? new Debugger.Debugger(this, Configuration.GdbStubPort) : null; System = new HOS.Horizon(this); diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs index 35967d274..0ccc35fc8 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Horizon.Bcat.Ipc service = null; } - return resultCode.ToHorizonResult(); + return resultCode.Horizon; } [CmifCommand(2)] @@ -71,7 +71,7 @@ namespace Ryujinx.Horizon.Bcat.Ipc service = null; } - return resultCode.ToHorizonResult(); + return resultCode.Horizon; } public void Dispose() diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs index 1559c833c..132453f05 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs @@ -22,19 +22,19 @@ namespace Ryujinx.Horizon.Bcat.Ipc [CmifCommand(0)] public Result Open(DirectoryName directoryName) { - return _libHacService.Get.Open(ref directoryName).ToHorizonResult(); + return _libHacService.Get.Open(ref directoryName).Horizon; } [CmifCommand(1)] public Result Read(out int entriesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span entriesBuffer) { - return _libHacService.Get.Read(out entriesRead, entriesBuffer).ToHorizonResult(); + return _libHacService.Get.Read(out entriesRead, entriesBuffer).Horizon; } [CmifCommand(2)] public Result GetCount(out int count) { - return _libHacService.Get.GetCount(out count).ToHorizonResult(); + return _libHacService.Get.GetCount(out count).Horizon; } public void Dispose() diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs index bd5c418d9..cb6cc3159 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs @@ -22,25 +22,25 @@ namespace Ryujinx.Horizon.Bcat.Ipc [CmifCommand(0)] public Result Open(DirectoryName directoryName, FileName fileName) { - return _libHacService.Get.Open(ref directoryName, ref fileName).ToHorizonResult(); + return _libHacService.Get.Open(ref directoryName, ref fileName).Horizon; } [CmifCommand(1)] public Result Read(long offset, out long bytesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span data) { - return _libHacService.Get.Read(out bytesRead, offset, data).ToHorizonResult(); + return _libHacService.Get.Read(out bytesRead, offset, data).Horizon; } [CmifCommand(2)] public Result GetSize(out long size) { - return _libHacService.Get.GetSize(out size).ToHorizonResult(); + return _libHacService.Get.GetSize(out size).Horizon; } [CmifCommand(3)] public Result GetDigest(out Digest digest) { - return _libHacService.Get.GetDigest(out digest).ToHorizonResult(); + return _libHacService.Get.GetDigest(out digest).Horizon; } public void Dispose() diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs index 356156fc1..b70d074d0 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Horizon.Bcat.Ipc service = null; } - return resultCode.ToHorizonResult(); + return resultCode.Horizon; } [CmifCommand(1)] @@ -54,13 +54,13 @@ namespace Ryujinx.Horizon.Bcat.Ipc service = null; } - return resultCode.ToHorizonResult(); + return resultCode.Horizon; } [CmifCommand(10)] public Result EnumerateDeliveryCacheDirectory(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span directoryNames) { - return _libHacService.Get.EnumerateDeliveryCacheDirectory(out count, directoryNames).ToHorizonResult(); + return _libHacService.Get.EnumerateDeliveryCacheDirectory(out count, directoryNames).Horizon; } public void Dispose() diff --git a/src/Ryujinx.Horizon/LibHacResultExtensions.cs b/src/Ryujinx.Horizon/LibHacResultExtensions.cs index 2abed197d..92c384141 100644 --- a/src/Ryujinx.Horizon/LibHacResultExtensions.cs +++ b/src/Ryujinx.Horizon/LibHacResultExtensions.cs @@ -4,9 +4,9 @@ namespace Ryujinx.Horizon { public static class LibHacResultExtensions { - public static Result ToHorizonResult(this LibHac.Result result) + extension(LibHac.Result libHacResult) { - return new Result((int)result.Module, (int)result.Description); + public Result Horizon => new((int)libHacResult.Module, (int)libHacResult.Description); } } } diff --git a/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs index 20d2a07ef..fdaa87956 100644 --- a/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs +++ b/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs @@ -123,10 +123,7 @@ namespace Ryujinx.Memory.Tracking Dirty = true; } - public IEnumerable GetHandles() - { - return _handles; - } + public IEnumerable Handles => _handles; public void ForceDirty(ulong address, ulong size) { diff --git a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs index b35de3a82..e878ff653 100644 --- a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs +++ b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs @@ -335,7 +335,7 @@ namespace Ryujinx.Tests.Memory IEnumerable[] handleGroups = [ - granular.GetHandles(), + granular.Handles, singlePages, doublePages ]; @@ -389,7 +389,7 @@ namespace Ryujinx.Tests.Memory Assert.IsTrue(throws); } - IEnumerable combinedHandles = combined.GetHandles(); + IEnumerable combinedHandles = combined.Handles; Assert.AreEqual(handleGroups[0].ElementAt(0), combinedHandles.ElementAt(3)); Assert.AreEqual(handleGroups[0].ElementAt(1), combinedHandles.ElementAt(4)); diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index 88c74eee5..382294cf7 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -59,10 +59,10 @@ namespace Ryujinx.Headless DisableDockedMode = !configurationState.System.EnableDockedMode; if (NeedsOverride(nameof(SystemLanguage))) - SystemLanguage = configurationState.System.Language.Value.ToHLE(); + SystemLanguage = configurationState.System.Language.Value.Horizon; if (NeedsOverride(nameof(SystemRegion))) - SystemRegion = configurationState.System.Region.Value.ToHLE(); + SystemRegion = configurationState.System.Region.Value.Horizon; if (NeedsOverride(nameof(SystemTimeZone))) SystemTimeZone = configurationState.System.TimeZone; diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 407c3a556..7dcfdb838 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -296,16 +296,16 @@ namespace Ryujinx.Ava // Check if region was overridden. if (CommandLineState.OverrideSystemRegion is not null) - if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out HLE.HOS.SystemState.RegionCode result)) + if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Region result)) { - ConfigurationState.Instance.System.Region.Value = result.ToUI(); + ConfigurationState.Instance.System.Region.Value = result; } //Check if language was overridden. if (CommandLineState.OverrideSystemLanguage is not null) - if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out HLE.HOS.SystemState.SystemLanguage result)) + if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Language result)) { - ConfigurationState.Instance.System.Language.Value = result.ToUI(); + ConfigurationState.Instance.System.Language.Value = result; } // Check if hardware-acceleration was overridden. diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs index 405400b81..2b4c8f991 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs @@ -916,8 +916,8 @@ namespace Ryujinx.Ava.Systems.Configuration public HleConfiguration CreateHleConfiguration() => new( System.DramSize, - System.Language.Value.ToHLE(), - System.Region.Value.ToHLE(), + System.Language.Value.Horizon, + System.Region.Value.Horizon, Graphics.VSyncMode, System.EnableDockedMode, System.EnablePtc, diff --git a/src/Ryujinx/Systems/Configuration/System/Language.cs b/src/Ryujinx/Systems/Configuration/System/Language.cs index ff1476b73..3087653e9 100644 --- a/src/Ryujinx/Systems/Configuration/System/Language.cs +++ b/src/Ryujinx/Systems/Configuration/System/Language.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.SystemState; using System.Text.Json.Serialization; namespace Ryujinx.Ava.Systems.Configuration.System @@ -28,10 +29,14 @@ namespace Ryujinx.Ava.Systems.Configuration.System public static class LanguageEnumHelper { - public static Language ToUI(this HLE.HOS.SystemState.SystemLanguage hleLanguage) - => (Language)hleLanguage; + extension(SystemLanguage hle) + { + public Language Ui => (Language)hle; + } - public static HLE.HOS.SystemState.SystemLanguage ToHLE(this Language uiLanguage) - => (HLE.HOS.SystemState.SystemLanguage)uiLanguage; + extension(Language ui) + { + public SystemLanguage Horizon => (SystemLanguage)ui; + } } } diff --git a/src/Ryujinx/Systems/Configuration/System/Region.cs b/src/Ryujinx/Systems/Configuration/System/Region.cs index 0c86093cc..2ba657876 100644 --- a/src/Ryujinx/Systems/Configuration/System/Region.cs +++ b/src/Ryujinx/Systems/Configuration/System/Region.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.SystemState; using System.Text.Json.Serialization; namespace Ryujinx.Ava.Systems.Configuration.System @@ -17,10 +18,14 @@ namespace Ryujinx.Ava.Systems.Configuration.System public static class RegionEnumHelper { - public static Region ToUI(this HLE.HOS.SystemState.RegionCode hleRegion) - => (Region)hleRegion; + extension(RegionCode hle) + { + public Region Ui => (Region)hle; + } - public static HLE.HOS.SystemState.RegionCode ToHLE(this Region uiRegion) - => (HLE.HOS.SystemState.RegionCode)uiRegion; + extension(Region ui) + { + public RegionCode Horizon => (RegionCode)ui; + } } } diff --git a/src/Ryujinx/UI/Helpers/Converters/XCITrimmerFileStatusDetailConverter.cs b/src/Ryujinx/UI/Helpers/Converters/XCITrimmerFileStatusDetailConverter.cs index b83fe485d..34734661b 100644 --- a/src/Ryujinx/UI/Helpers/Converters/XCITrimmerFileStatusDetailConverter.cs +++ b/src/Ryujinx/UI/Helpers/Converters/XCITrimmerFileStatusDetailConverter.cs @@ -29,9 +29,11 @@ namespace Ryujinx.Ava.UI.Helpers return null; } - return app.PercentageProgress != null ? null : - app.ProcessingOutcome is not OperationOutcome.Successful and not OperationOutcome.Undetermined ? app.ProcessingOutcome.ToLocalisedText() : - null; + return app.PercentageProgress != null + ? null + : app.ProcessingOutcome is not OperationOutcome.Successful and not OperationOutcome.Undetermined + ? app.ProcessingOutcome.LocalizedText + : null; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/src/Ryujinx/UI/Helpers/XCITrimmerOperationOutcomeHelper.cs b/src/Ryujinx/UI/Helpers/XCITrimmerOperationOutcomeHelper.cs index 1a0e126c8..d58056469 100644 --- a/src/Ryujinx/UI/Helpers/XCITrimmerOperationOutcomeHelper.cs +++ b/src/Ryujinx/UI/Helpers/XCITrimmerOperationOutcomeHelper.cs @@ -5,32 +5,23 @@ namespace Ryujinx.Ava.UI.Helpers { public static class XCIFileTrimmerOperationOutcomeExtensions { - public static string ToLocalisedText(this OperationOutcome operationOutcome) + extension(OperationOutcome opOutcome) { - switch (operationOutcome) + public string LocalizedText => opOutcome switch { - case OperationOutcome.NoTrimNecessary: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileNoTrimNecessary]; - case OperationOutcome.NoUntrimPossible: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileNoUntrimPossible]; - case OperationOutcome.ReadOnlyFileCannotFix: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileReadOnlyFileCannotFix]; - case OperationOutcome.FreeSpaceCheckFailed: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileFreeSpaceCheckFailed]; - case OperationOutcome.InvalidXCIFile: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileInvalidXCIFile]; - case OperationOutcome.FileIOWriteError: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileFileIOWriteError]; - case OperationOutcome.FileSizeChanged: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileFileSizeChanged]; - case OperationOutcome.Cancelled: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileCancelled]; - case OperationOutcome.Undetermined: - return LocaleManager.Instance[LocaleKeys.TrimXCIFileFileUndertermined]; - case OperationOutcome.Successful: - default: - return null; - } + OperationOutcome.NoTrimNecessary => LocaleManager.Instance[LocaleKeys.TrimXCIFileNoTrimNecessary], + OperationOutcome.NoUntrimPossible => LocaleManager.Instance[LocaleKeys.TrimXCIFileNoUntrimPossible], + OperationOutcome.ReadOnlyFileCannotFix => LocaleManager.Instance[ + LocaleKeys.TrimXCIFileReadOnlyFileCannotFix], + OperationOutcome.FreeSpaceCheckFailed => LocaleManager.Instance[ + LocaleKeys.TrimXCIFileFreeSpaceCheckFailed], + OperationOutcome.InvalidXCIFile => LocaleManager.Instance[LocaleKeys.TrimXCIFileInvalidXCIFile], + OperationOutcome.FileIOWriteError => LocaleManager.Instance[LocaleKeys.TrimXCIFileFileIOWriteError], + OperationOutcome.FileSizeChanged => LocaleManager.Instance[LocaleKeys.TrimXCIFileFileSizeChanged], + OperationOutcome.Cancelled => LocaleManager.Instance[LocaleKeys.TrimXCIFileCancelled], + OperationOutcome.Undetermined => LocaleManager.Instance[LocaleKeys.TrimXCIFileFileUndertermined], + _ => null + }; } } } diff --git a/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs index 727294992..47a99d886 100644 --- a/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs @@ -11,13 +11,17 @@ namespace Ryujinx.Ava.UI.ViewModels { public partial class AboutWindowViewModel : BaseModel, IDisposable { - [ObservableProperty] private Bitmap _gitLabLogo; - [ObservableProperty] private Bitmap _discordLogo; - [ObservableProperty] private string _version; + [ObservableProperty] public partial Bitmap GitLabLogo { get; set; } - public string Developers => "GreemDev, LotP"; + [ObservableProperty] public partial Bitmap DiscordLogo { get; set; } - public string FormerDevelopers => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.AboutPageDeveloperListMore, "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz"); + [ObservableProperty] public partial string Version { get; set; } + + public static string Developers => "GreemDev, LotP"; + + public static string FormerDevelopers => LocaleManager.Instance.UpdateAndGetDynamicValue( + LocaleKeys.AboutPageDeveloperListMore, + "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz"); public AboutWindowViewModel() { @@ -36,7 +40,8 @@ namespace Ryujinx.Ava.UI.ViewModels private void UpdateLogoTheme(string theme) { - bool isDarkTheme = theme == "Dark" || (theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark); + bool isDarkTheme = theme == "Dark" || + (theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark); string themeName = isDarkTheme ? "Dark" : "Light"; diff --git a/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs index 0ba071475..053972c2c 100644 --- a/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs @@ -2,6 +2,7 @@ using Avalonia; using Avalonia.Collections; using Avalonia.Media.Imaging; using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Models.Amiibo; using Ryujinx.Ava.UI.Helpers; @@ -23,7 +24,7 @@ using System.Threading.Tasks; namespace Ryujinx.Ava.UI.ViewModels { - public class AmiiboWindowViewModel : BaseModel, IDisposable + public partial class AmiiboWindowViewModel : BaseModel, IDisposable { // ReSharper disable once InconsistentNaming private static bool _cachedUseRandomUuid; @@ -36,17 +37,13 @@ namespace Ryujinx.Ava.UI.ViewModels private readonly HttpClient _httpClient; private readonly AmiiboWindow _owner; - private Bitmap _amiiboImage; private List _amiiboList; private AvaloniaList _amiibos; private ObservableCollection _amiiboSeries; private int _amiiboSelectedIndex; private int _seriesSelectedIndex; - private bool _enableScanning; private bool _showAllAmiibo; - private bool _useRandomUuid = _cachedUseRandomUuid; - private string _usage; private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); @@ -83,14 +80,14 @@ namespace Ryujinx.Ava.UI.ViewModels public bool UseRandomUuid { - get => _useRandomUuid; + get; set { - _cachedUseRandomUuid = _useRandomUuid = value; + _cachedUseRandomUuid = field = value; OnPropertyChanged(); } - } + } = _cachedUseRandomUuid; public bool ShowAllAmiibo { @@ -154,38 +151,14 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public Bitmap AmiiboImage - { - get => _amiiboImage; - set - { - _amiiboImage = value; + [ObservableProperty] + public partial Bitmap AmiiboImage { get; set; } - OnPropertyChanged(); - } - } + [ObservableProperty] + public partial string Usage { get; set; } - public string Usage - { - get => _usage; - set - { - _usage = value; - - OnPropertyChanged(); - } - } - - public bool EnableScanning - { - get => _enableScanning; - set - { - _enableScanning = value; - - OnPropertyChanged(); - } - } + [ObservableProperty] + public partial bool EnableScanning { get; set; } public void Scan() { diff --git a/src/Ryujinx/UI/ViewModels/DlcSelectViewModel.cs b/src/Ryujinx/UI/ViewModels/DlcSelectViewModel.cs index 643614d6e..b280c96c9 100644 --- a/src/Ryujinx/UI/ViewModels/DlcSelectViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/DlcSelectViewModel.cs @@ -7,14 +7,16 @@ namespace Ryujinx.Ava.UI.ViewModels { public partial class DlcSelectViewModel : BaseModel { - [ObservableProperty] private DownloadableContentModel[] _dlcs; + [ObservableProperty] + public partial DownloadableContentModel[] Dlcs { get; set; } #nullable enable - [ObservableProperty] private DownloadableContentModel? _selectedDlc; + [ObservableProperty] + public partial DownloadableContentModel? SelectedDlc { get; set; } #nullable disable public DlcSelectViewModel(ulong titleId, ApplicationLibrary appLibrary) { - _dlcs = appLibrary.FindDlcsFor(titleId) + Dlcs = appLibrary.FindDlcsFor(titleId) .OrderBy(it => it.IsBundled ? 0 : 1) .ThenBy(it => it.TitleId) .ToArray(); diff --git a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs index c048b481c..39e53184f 100644 --- a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -19,10 +19,14 @@ namespace Ryujinx.Ava.UI.ViewModels public partial class DownloadableContentManagerViewModel : BaseModel { private readonly ApplicationLibrary _applicationLibrary; - private AvaloniaList _downloadableContents = []; - [ObservableProperty] private AvaloniaList _selectedDownloadableContents = []; - [ObservableProperty] private AvaloniaList _views = []; - [ObservableProperty] private bool _showBundledContentNotice = false; + [ObservableProperty] + public partial AvaloniaList SelectedDownloadableContents { get; set; } = []; + + [ObservableProperty] + public partial AvaloniaList Views { get; set; } = []; + + [ObservableProperty] + public partial bool ShowBundledContentNotice { get; set; } = false; private string _search; private readonly ApplicationData _applicationData; @@ -30,15 +34,15 @@ namespace Ryujinx.Ava.UI.ViewModels public AvaloniaList DownloadableContents { - get => _downloadableContents; + get; set { - _downloadableContents = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(UpdateCount)); Sort(); } - } + } = []; public string Search { @@ -51,10 +55,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public string UpdateCount - { - get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); - } + public string UpdateCount => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); public DownloadableContentManagerViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData) { diff --git a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs index 61d3ffd59..2949b69a8 100644 --- a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs @@ -4,55 +4,50 @@ using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Views.Input; using Ryujinx.Common.Utilities; using Ryujinx.UI.Views.Input; -using System.Drawing; namespace Ryujinx.Ava.UI.ViewModels.Input { public partial class ControllerInputViewModel : BaseModel { - private GamepadInputConfig _config; public GamepadInputConfig Config { - get => _config; + get; set { - _config = value; + field = value; OnPropertyChanged(); } } - private StickVisualizer _visualizer; public StickVisualizer Visualizer { - get => _visualizer; + get; set { - _visualizer = value; + field = value; OnPropertyChanged(); } } - private bool _isLeft; public bool IsLeft { - get => _isLeft; + get; set { - _isLeft = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(HasSides)); } } - private bool _isRight; public bool IsRight { - get => _isRight; + get; set { - _isRight = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(HasSides)); } @@ -60,8 +55,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public bool HasSides => IsLeft ^ IsRight; - [ObservableProperty] private SvgImage _image; - + [ObservableProperty] + public partial SvgImage Image { get; set; } public InputViewModel ParentModel { get; } public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config, StickVisualizer visualizer) @@ -75,7 +70,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input if (args.PropertyName is nameof(Config.UseRainbowLed)) { if (Config is { UseRainbowLed: true, TurnOffLed: false, EnableLedChanging: true }) - Rainbow.Updated += (ref Color color) => ParentModel.SelectedGamepad.SetLed((uint)color.ToArgb()); + Rainbow.Updated += (ref color) => ParentModel.SelectedGamepad.SetLed((uint)color.ToArgb()); else { Rainbow.Reset(); diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index c89846dc4..289dc0e9c 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -48,36 +48,41 @@ namespace Ryujinx.Ava.UI.ViewModels.Input private int _controller; private string _controllerImage; private int _device; - private object _configViewModel; private bool _isChangeTrackingActive; - private string _chosenProfile; - [ObservableProperty] private bool _isModified; - [ObservableProperty] private string _profileName; - [ObservableProperty] private bool _notificationIsVisible; // Automatically call the NotificationView property with OnPropertyChanged() - [ObservableProperty] private string _notificationText; // Automatically call the NotificationText property with OnPropertyChanged() + [ObservableProperty] + public partial bool IsModified { get; set; } + + [ObservableProperty] + public partial string ProfileName { get; set; } + + [ObservableProperty] + public partial bool NotificationIsVisible { get; set; } // Automatically call the NotificationView property with OnPropertyChanged() + + [ObservableProperty] + public partial string NotificationText { get; set; } // Automatically call the NotificationText property with OnPropertyChanged() + private bool _isLoaded; private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public IGamepadDriver AvaloniaKeyboardDriver { get; } - private IGamepad _selectedGamepad; - public IGamepad SelectedGamepad { - get => _selectedGamepad; + get; private set { Rainbow.Reset(); - _selectedGamepad = value; + field = value; if (ConfigViewModel is ControllerInputViewModel { Config.UseRainbowLed: true }) - Rainbow.Updated += (ref Color color) => _selectedGamepad.SetLed((uint)color.ToArgb()); + Rainbow.Updated += (ref Color color) => field.SetLed((uint)color.ToArgb()); OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed)); } } + public StickVisualizer VisualStick { get; private set; } public ObservableCollection PlayerIndexes { get; set; } @@ -99,15 +104,15 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense"); public event Action NotifyChangesEvent; - + public string ChosenProfile { - get => _chosenProfile; + get; set { // When you select a profile, the settings from the profile will be applied. // To save the settings, you still need to click the apply button - _chosenProfile = value; + field = value; LoadProfile(); OnPropertyChanged(); } @@ -115,10 +120,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public object ConfigViewModel { - get => _configViewModel; + get; set { - _configViewModel = value; + field = value; VisualStick.UpdateConfig(value); diff --git a/src/Ryujinx/UI/ViewModels/Input/KeyboardInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/KeyboardInputViewModel.cs index bab8db7ce..178e2c955 100644 --- a/src/Ryujinx/UI/ViewModels/Input/KeyboardInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/KeyboardInputViewModel.cs @@ -6,49 +6,45 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { public partial class KeyboardInputViewModel : BaseModel { - private KeyboardInputConfig _config; public KeyboardInputConfig Config { - get => _config; + get; set { - _config = value; + field = value; OnPropertyChanged(); } } - private StickVisualizer _visualizer; public StickVisualizer Visualizer { - get => _visualizer; + get; set { - _visualizer = value; + field = value; OnPropertyChanged(); } } - private bool _isLeft; public bool IsLeft { - get => _isLeft; + get; set { - _isLeft = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(HasSides)); } } - private bool _isRight; public bool IsRight { - get => _isRight; + get; set { - _isRight = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(HasSides)); } @@ -56,7 +52,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public bool HasSides => IsLeft ^ IsRight; - [ObservableProperty] private SvgImage _image; + [ObservableProperty] + public partial SvgImage Image { get; set; } public readonly InputViewModel ParentModel; diff --git a/src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs index 71c404c21..516b892b5 100644 --- a/src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs @@ -23,8 +23,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input ParentModel.SelectedGamepad.SetLed(LedColor.ToUInt32()); }); - [ObservableProperty] private bool _enableLedChanging; - [ObservableProperty] private Color _ledColor; + [ObservableProperty] + public partial bool EnableLedChanging { get; set; } + + [ObservableProperty] + public partial Color LedColor { get; set; } public string RainbowSpeedText => RainbowSpeed.ToString(CultureInfo.CurrentCulture).Truncate(4, string.Empty); @@ -41,27 +44,23 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public bool ShowLedColorPicker => !TurnOffLed && !UseRainbowLed; - private bool _turnOffLed; - public bool TurnOffLed { - get => _turnOffLed; + get; set { - _turnOffLed = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowLedColorPicker)); } } - private bool _useRainbowLed; - public bool UseRainbowLed { - get => _useRainbowLed; + get; set { - _useRainbowLed = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowLedColorPicker)); } diff --git a/src/Ryujinx/UI/ViewModels/Input/MotionInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/MotionInputViewModel.cs index ba8686831..ad9a70eaa 100644 --- a/src/Ryujinx/UI/ViewModels/Input/MotionInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/MotionInputViewModel.cs @@ -4,20 +4,28 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { public partial class MotionInputViewModel : BaseModel { - [ObservableProperty] private int _slot; + [ObservableProperty] + public partial int Slot { get; set; } - [ObservableProperty] private int _altSlot; + [ObservableProperty] + public partial int AltSlot { get; set; } - [ObservableProperty] private string _dsuServerHost; + [ObservableProperty] + public partial string DsuServerHost { get; set; } - [ObservableProperty] private int _dsuServerPort; + [ObservableProperty] + public partial int DsuServerPort { get; set; } - [ObservableProperty] private bool _mirrorInput; + [ObservableProperty] + public partial bool MirrorInput { get; set; } - [ObservableProperty] private int _sensitivity; + [ObservableProperty] + public partial int Sensitivity { get; set; } - [ObservableProperty] private double _gyroDeadzone; + [ObservableProperty] + public partial double GyroDeadzone { get; set; } - [ObservableProperty] private bool _enableCemuHookMotion; + [ObservableProperty] + public partial bool EnableCemuHookMotion { get; set; } } } diff --git a/src/Ryujinx/UI/ViewModels/Input/RumbleInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/RumbleInputViewModel.cs index c4158fced..e2323f567 100644 --- a/src/Ryujinx/UI/ViewModels/Input/RumbleInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/RumbleInputViewModel.cs @@ -4,8 +4,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { public partial class RumbleInputViewModel : BaseModel { - [ObservableProperty] private float _strongRumble; + [ObservableProperty] + public partial float StrongRumble { get; set; } - [ObservableProperty] private float _weakRumble; + [ObservableProperty] + public partial float WeakRumble { get; set; } } } diff --git a/src/Ryujinx/UI/ViewModels/LdnGamesListViewModel.cs b/src/Ryujinx/UI/ViewModels/LdnGamesListViewModel.cs index cdce9262d..91ebccabb 100644 --- a/src/Ryujinx/UI/ViewModels/LdnGamesListViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/LdnGamesListViewModel.cs @@ -91,10 +91,8 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(nameof(VisibleEntries)); } - [ObservableProperty] private bool _isRefreshing; - private bool _onlyShowForOwnedGames; - private bool _onlyShowPublicGames = true; - private bool _onlyShowJoinableGames = true; + [ObservableProperty] + public partial bool IsRefreshing { get; set; } public async Task RefreshAsync() { @@ -109,12 +107,12 @@ namespace Ryujinx.Ava.UI.ViewModels public bool OnlyShowForOwnedGames { - get => _onlyShowForOwnedGames; + get; set { OnPropertyChanging(); OnPropertyChanging(nameof(VisibleEntries)); - _onlyShowForOwnedGames = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(VisibleEntries)); } @@ -122,29 +120,29 @@ namespace Ryujinx.Ava.UI.ViewModels public bool OnlyShowPublicGames { - get => _onlyShowPublicGames; + get; set { OnPropertyChanging(); OnPropertyChanging(nameof(VisibleEntries)); - _onlyShowPublicGames = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(VisibleEntries)); } - } + } = true; public bool OnlyShowJoinableGames { - get => _onlyShowJoinableGames; + get; set { OnPropertyChanging(); OnPropertyChanging(nameof(VisibleEntries)); - _onlyShowJoinableGames = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(VisibleEntries)); } - } + } = true; public void NameSorting(int nameSort = 0) diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index b665f23c9..2236b27f6 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -64,55 +64,101 @@ namespace Ryujinx.Ava.UI.ViewModels public partial class MainWindowViewModel : BaseModel { private const int HotKeyPressDelayMs = 500; + private delegate int LoadContentFromFolderDelegate(List dirs, out int numRemoved); - [ObservableProperty] private ObservableCollectionExtended _applications; - [ObservableProperty] private string _aspectRatioStatusText; - [ObservableProperty] private string _loadHeading; - [ObservableProperty] private string _cacheLoadStatus; - [ObservableProperty] private string _dockedStatusText; - [ObservableProperty] private string _fifoStatusText; - [ObservableProperty] private string _gameStatusText; - [ObservableProperty] private string _volumeStatusText; - [ObservableProperty] private string _gpuNameText; - [ObservableProperty] private string _backendText; - [ObservableProperty] private string _shaderCountText; - [ObservableProperty] private bool _showShaderCompilationHint; - [ObservableProperty] private bool _isFullScreen; - [ObservableProperty] private int _progressMaximum; - [ObservableProperty] private int _progressValue; - [ObservableProperty] private bool _showMenuAndStatusBar = true; - [ObservableProperty] private bool _showStatusSeparator; - [ObservableProperty] private Brush _progressBarForegroundColor; - [ObservableProperty] private Brush _progressBarBackgroundColor; - [ObservableProperty] private Brush _vSyncModeColor; -#nullable enable - [ObservableProperty] private byte[]? _selectedIcon; -#nullable disable - [ObservableProperty] private int _statusBarProgressMaximum; - [ObservableProperty] private int _statusBarProgressValue; - [ObservableProperty] private string _statusBarProgressStatusText; - [ObservableProperty] private bool _statusBarProgressStatusVisible; - [ObservableProperty] private bool _isPaused; - [ObservableProperty] private bool _isLoadingIndeterminate = true; - [ObservableProperty] private bool _showAll; - [ObservableProperty] private string _lastScannedAmiiboId; + [ObservableProperty] public partial ObservableCollectionExtended Applications { get; set; } + + [ObservableProperty] public partial string AspectRatioStatusText { get; set; } + + [ObservableProperty] public partial string LoadHeading { get; set; } + + [ObservableProperty] public partial string CacheLoadStatus { get; set; } + + [ObservableProperty] public partial string DockedStatusText { get; set; } + + [ObservableProperty] public partial string FifoStatusText { get; set; } + + [ObservableProperty] public partial string GameStatusText { get; set; } + + [ObservableProperty] public partial string VolumeStatusText { get; set; } + + [ObservableProperty] public partial string GpuNameText { get; set; } + + [ObservableProperty] public partial string BackendText { get; set; } + + [ObservableProperty] public partial string ShaderCountText { get; set; } + + [ObservableProperty] public partial bool ShowShaderCompilationHint { get; set; } + + [ObservableProperty] public partial bool IsFullScreen { get; set; } + + [ObservableProperty] public partial int ProgressMaximum { get; set; } + + [ObservableProperty] public partial int ProgressValue { get; set; } + + [ObservableProperty] public partial bool ShowMenuAndStatusBar { get; set; } = true; + + [ObservableProperty] public partial bool ShowStatusSeparator { get; set; } + + [ObservableProperty] public partial Brush ProgressBarForegroundColor { get; set; } + + [ObservableProperty] public partial Brush ProgressBarBackgroundColor { get; set; } + +#pragma warning disable MVVMTK0042 // Must stay a normal observable field declaration since this is used as an out parameter target [ObservableProperty] private ReadOnlyObservableCollection _appsObservableList; - [ObservableProperty] private long _lastFullscreenToggle = Environment.TickCount64; - [ObservableProperty] private bool _showContent = true; - [ObservableProperty] private float _volumeBeforeMute; - [ObservableProperty] private bool _areMimeTypesRegistered = FileAssociationHelper.AreMimeTypesRegistered; - [ObservableProperty] private Cursor _cursor; - [ObservableProperty] private string _title; - [ObservableProperty] private WindowState _windowState; - [ObservableProperty] private double _windowWidth; - [ObservableProperty] private double _windowHeight; - [ObservableProperty] private bool _isActive; - [ObservableProperty] private bool _isSubMenuOpen; - [ObservableProperty] private ApplicationContextMenu _listAppContextMenu; - [ObservableProperty] private ApplicationContextMenu _gridAppContextMenu; - [ObservableProperty] private bool _isRyuLdnEnabled; - [ObservableProperty] private bool _updateAvailable; +#pragma warning restore MVVMTK0042 + + [ObservableProperty] public partial Brush VSyncModeColor { get; set; } +#nullable enable + [ObservableProperty] public partial byte[]? SelectedIcon { get; set; } +#nullable disable + [ObservableProperty] public partial int StatusBarProgressMaximum { get; set; } + + [ObservableProperty] public partial int StatusBarProgressValue { get; set; } + + [ObservableProperty] public partial string StatusBarProgressStatusText { get; set; } + + [ObservableProperty] public partial bool StatusBarProgressStatusVisible { get; set; } + + [ObservableProperty] public partial bool IsPaused { get; set; } + + [ObservableProperty] public partial bool IsLoadingIndeterminate { get; set; } = true; + + [ObservableProperty] public partial bool ShowAll { get; set; } + + [ObservableProperty] public partial string LastScannedAmiiboId { get; set; } + + [ObservableProperty] + public partial long LastFullscreenToggle { get; set; } = Environment.TickCount64; + [ObservableProperty] public partial bool ShowContent { get; set; } = true; + + [ObservableProperty] public partial float VolumeBeforeMute { get; set; } + + [ObservableProperty] + public partial bool AreMimeTypesRegistered { get; set; } = FileAssociationHelper.AreMimeTypesRegistered; + + [ObservableProperty] public partial Cursor Cursor { get; set; } + + [ObservableProperty] public partial string Title { get; set; } + + [ObservableProperty] public partial WindowState WindowState { get; set; } + + [ObservableProperty] public partial double WindowWidth { get; set; } + + [ObservableProperty] public partial double WindowHeight { get; set; } + + [ObservableProperty] public partial bool IsActive { get; set; } + + [ObservableProperty] public partial bool IsSubMenuOpen { get; set; } + + [ObservableProperty] public partial ApplicationContextMenu ListAppContextMenu { get; set; } + + [ObservableProperty] public partial ApplicationContextMenu GridAppContextMenu { get; set; } + + [ObservableProperty] public partial bool IsRyuLdnEnabled { get; set; } + + [ObservableProperty] public partial bool UpdateAvailable { get; set; } public static AsyncRelayCommand UpdateCommand { get; } = Commands.Create(async () => { @@ -120,27 +166,17 @@ namespace Ryujinx.Ava.UI.ViewModels await Updater.BeginUpdateAsync(true); }); - private bool _showTotalTimePlayed; - private bool _showLoadProgress; private bool _isGameRunning; - private bool _isAmiiboRequested; - private bool _isAmiiboBinRequested; private string _searchText; private Timer _searchTimer; - private string _vSyncModeText; private string _showUiKey = "F4"; private string _pauseKey = "F5"; private string _screenshotKey = "F8"; private float _volume; - private bool _isAppletMenuActive; - private bool _statusBarVisible; - private bool _canUpdate = true; private ApplicationData _currentApplicationData; private readonly AutoResetEvent _rendererWaitEvent; private int _customVSyncInterval; private int _customVSyncIntervalPercentageProxy; - private ApplicationData _listSelectedApplication; - private ApplicationData _gridSelectedApplication; // Key is Title ID /// @@ -267,20 +303,20 @@ namespace Ryujinx.Ava.UI.ViewModels public bool CanUpdate { - get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(); + get => field && EnableNonGameRunningControls && Updater.CanUpdate(); set { - _canUpdate = value; + field = value; OnPropertyChanged(); } - } + } = true; public bool StatusBarVisible { - get => _statusBarVisible && EnableNonGameRunningControls; + get => field && EnableNonGameRunningControls; set { - _statusBarVisible = value; + field = value; OnPropertyChanged(); } @@ -312,20 +348,21 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsAmiiboRequested { - get => _isAmiiboRequested && _isGameRunning; + get => field && _isGameRunning; set { - _isAmiiboRequested = value; + field = value; OnPropertyChanged(); } } + public bool IsAmiiboBinRequested { - get => _isAmiiboBinRequested && _isGameRunning; + get => field && _isGameRunning; set { - _isAmiiboBinRequested = value; + field = value; OnPropertyChanged(); } @@ -335,10 +372,10 @@ namespace Ryujinx.Ava.UI.ViewModels public bool ShowLoadProgress { - get => _showLoadProgress; + get; set { - _showLoadProgress = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowFirmwareStatus)); @@ -360,24 +397,24 @@ namespace Ryujinx.Ava.UI.ViewModels public bool ShowTotalTimePlayed { - get => _showTotalTimePlayed && EnableNonGameRunningControls; + get => field && EnableNonGameRunningControls; set { - _showTotalTimePlayed = value; + field = value; OnPropertyChanged(); } } public ApplicationData ListSelectedApplication { - get => _listSelectedApplication; + get; set { - _listSelectedApplication = value; + field = value; - if (_listSelectedApplication != null && ListAppContextMenu == null) + if (field != null && ListAppContextMenu == null) ListAppContextMenu = new ApplicationContextMenu(); - else if (_listSelectedApplication == null && ListAppContextMenu != null) + else if (field == null && ListAppContextMenu != null) ListAppContextMenu = null!; OnPropertyChanged(); @@ -386,14 +423,14 @@ namespace Ryujinx.Ava.UI.ViewModels public ApplicationData GridSelectedApplication { - get => _gridSelectedApplication; + get; set { - _gridSelectedApplication = value; + field = value; - if (_gridSelectedApplication != null && GridAppContextMenu == null) + if (field != null && GridAppContextMenu == null) GridAppContextMenu = new ApplicationContextMenu(); - else if (_gridSelectedApplication == null && GridAppContextMenu != null) + else if (field == null && GridAppContextMenu != null) GridAppContextMenu = null!; OnPropertyChanged(); @@ -422,13 +459,18 @@ namespace Ryujinx.Ava.UI.ViewModels public bool HasDlc => ApplicationLibrary.HasDlcs(SelectedApplication.Id); - public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; + public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && + SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; - public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; + public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && + SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; - public bool TrimXCIEnabled => XCIFileTrimmer.CanTrim(SelectedApplication.Path, new XCITrimmerLog.MainWindow(this)); + public bool TrimXCIEnabled => + XCIFileTrimmer.CanTrim(SelectedApplication.Path, new XCITrimmerLog.MainWindow(this)); - public bool OpenBcatSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; + public bool OpenBcatSaveDirectoryEnabled => SelectedApplication.HasControlHolder && + SelectedApplication.ControlHolder.Value + .BcatDeliveryCacheStorageSize > 0; public bool ShowCustomVSyncIntervalPicker => _isGameRunning && AppHost.Device.VSyncMode == VSyncMode.Custom; @@ -466,7 +508,6 @@ namespace Ryujinx.Ava.UI.ViewModels } set { - } } @@ -492,10 +533,10 @@ namespace Ryujinx.Ava.UI.ViewModels public string VSyncModeText { - get => _vSyncModeText; + get; set { - _vSyncModeText = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker)); @@ -524,10 +565,10 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsAppletMenuActive { - get => _isAppletMenuActive && EnableNonGameRunningControls; + get => field && EnableNonGameRunningControls; set { - _isAppletMenuActive = value; + field = value; OnPropertyChanged(); } @@ -799,7 +840,8 @@ namespace Ryujinx.Ava.UI.ViewModels #region PrivateMethods - private static SortExpressionComparer CreateComparer(bool ascending, Func selector) => + private static SortExpressionComparer CreateComparer(bool ascending, + Func selector) => ascending ? SortExpressionComparer.Ascending(selector) : SortExpressionComparer.Descending(selector); @@ -808,15 +850,15 @@ namespace Ryujinx.Ava.UI.ViewModels => SortMode switch { #pragma warning disable IDE0055 // Disable formatting - ApplicationSort.Title => CreateComparer(IsAscending, app => app.Name), - ApplicationSort.Developer => CreateComparer(IsAscending, app => app.Developer), - ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending), + ApplicationSort.Title => CreateComparer(IsAscending, app => app.Name), + ApplicationSort.Developer => CreateComparer(IsAscending, app => app.Developer), + ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending), ApplicationSort.TotalTimePlayed => new TimePlayedSortComparer(IsAscending), - ApplicationSort.FileType => CreateComparer(IsAscending, app => app.FileExtension), - ApplicationSort.FileSize => CreateComparer(IsAscending, app => app.FileSize), - ApplicationSort.Path => CreateComparer(IsAscending, app => app.Path), - ApplicationSort.Favorite => CreateComparer(IsAscending, app => new AppListFavoriteComparable(app)), - ApplicationSort.TitleId => CreateComparer(IsAscending, app => app.Id), + ApplicationSort.FileType => CreateComparer(IsAscending, app => app.FileExtension), + ApplicationSort.FileSize => CreateComparer(IsAscending, app => app.FileSize), + ApplicationSort.Path => CreateComparer(IsAscending, app => app.Path), + ApplicationSort.Favorite => CreateComparer(IsAscending, app => new AppListFavoriteComparable(app)), + ApplicationSort.TitleId => CreateComparer(IsAscending, app => app.Id), _ => null, #pragma warning restore IDE0055 }; @@ -848,7 +890,8 @@ namespace Ryujinx.Ava.UI.ViewModels CompareInfo compareInfo = CultureInfo.CurrentCulture.CompareInfo; - return compareInfo.IndexOf(app.Name, _searchText, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0; + return compareInfo.IndexOf(app.Name, _searchText, + CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0; } return false; @@ -862,21 +905,27 @@ namespace Ryujinx.Ava.UI.ViewModels if (firmwareVersion == null) { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage, filename)); + await ContentDialogHelper.CreateErrorDialog( + LocaleManager.Instance.UpdateAndGetDynamicValue( + LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage, filename)); return; } - string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle, firmwareVersion.VersionString); - string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage, firmwareVersion.VersionString); + string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue( + LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle, firmwareVersion.VersionString); + string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue( + LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage, firmwareVersion.VersionString); SystemVersion currentVersion = ContentManager.GetCurrentFirmwareVersion(); if (currentVersion != null) { - dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage, currentVersion.VersionString); + dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue( + LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage, currentVersion.VersionString); } - dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage]; + dialogMessage += + LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage]; UserResult result = await ContentDialogHelper.CreateConfirmationDialog( dialogTitle, @@ -885,7 +934,8 @@ namespace Ryujinx.Ava.UI.ViewModels LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - UpdateWaitWindow waitingDialog = new(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]); + UpdateWaitWindow waitingDialog = new(dialogTitle, + LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]); if (result == UserResult.Yes) { @@ -906,7 +956,9 @@ namespace Ryujinx.Ava.UI.ViewModels { waitingDialog.Close(); - string message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage, firmwareVersion.VersionString); + string message = LocaleManager.Instance.UpdateAndGetDynamicValue( + LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage, + firmwareVersion.VersionString); await ContentDialogHelper.CreateInfoDialog( dialogTitle, @@ -919,7 +971,8 @@ namespace Ryujinx.Ava.UI.ViewModels // Purge Applet Cache. - DirectoryInfo miiEditorCacheFolder = new(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); + DirectoryInfo miiEditorCacheFolder = new(Path.Combine(AppDataManager.GamesDirPath, + "0100000000001009", "cache")); if (miiEditorCacheFolder.Exists) { @@ -940,10 +993,7 @@ namespace Ryujinx.Ava.UI.ViewModels { RefreshFirmwareStatus(); } - }) - { - Name = "GUI.FirmwareInstallerThread", - }; + }) { Name = "GUI.FirmwareInstallerThread", }; thread.Start(); } @@ -968,17 +1018,22 @@ namespace Ryujinx.Ava.UI.ViewModels try { string systemDirectory = AppDataManager.KeysDirPath; - if (AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && Directory.Exists(AppDataManager.KeysDirPathUser)) + if (AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && + Directory.Exists(AppDataManager.KeysDirPathUser)) { systemDirectory = AppDataManager.KeysDirPathUser; } - string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallTitle); - string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage); - + string dialogTitle = + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallTitle); + string dialogMessage = + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage); + if (ContentManager.AreKeysAlredyPresent(systemDirectory)) { - dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallSubMessage); + dialogMessage += + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys + .DialogKeysInstallerKeysInstallSubMessage); } dialogMessage += LocaleManager.Instance[LocaleKeys.DialogKeysInstallerKeysInstallConfirmMessage]; @@ -990,7 +1045,8 @@ namespace Ryujinx.Ava.UI.ViewModels LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - UpdateWaitWindow waitingDialog = new(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogKeysInstallerKeysInstallWaitMessage]); + UpdateWaitWindow waitingDialog = new(dialogTitle, + LocaleManager.Instance[LocaleKeys.DialogKeysInstallerKeysInstallWaitMessage]); if (result == UserResult.Yes) { @@ -1011,7 +1067,9 @@ namespace Ryujinx.Ava.UI.ViewModels { waitingDialog.Close(); - string message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallSuccessMessage); + string message = + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys + .DialogKeysInstallerKeysInstallSuccessMessage); await ContentDialogHelper.CreateInfoDialog( dialogTitle, @@ -1032,7 +1090,8 @@ namespace Ryujinx.Ava.UI.ViewModels string message = ex.Message; if (ex is FormatException) { - message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysNotFoundErrorMessage, filename); + message = LocaleManager.Instance.UpdateAndGetDynamicValue( + LocaleKeys.DialogKeysInstallerKeysNotFoundErrorMessage, filename); } await ContentDialogHelper.CreateErrorDialog(message); @@ -1042,10 +1101,7 @@ namespace Ryujinx.Ava.UI.ViewModels { VirtualFileSystem.ReloadKeySet(); } - }) - { - Name = "GUI.KeysInstallerThread", - }; + }) { Name = "GUI.KeysInstallerThread", }; thread.Start(); } @@ -1064,6 +1120,7 @@ namespace Ryujinx.Ava.UI.ViewModels await ContentDialogHelper.CreateErrorDialog(ex.Message); } } + private void ProgressHandler(T state, int current, int total) where T : Enum { Dispatcher.UIThread.Post(() => @@ -1083,7 +1140,8 @@ namespace Ryujinx.Ava.UI.ViewModels IsLoadingIndeterminate = false; break; case LoadState.Loaded: - LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name); + LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, + _currentApplicationData.Name); IsLoadingIndeterminate = true; CacheLoadStatus = string.Empty; break; @@ -1104,7 +1162,8 @@ namespace Ryujinx.Ava.UI.ViewModels IsLoadingIndeterminate = false; break; case ShaderCacheLoadingState.Loaded: - LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name); + LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, + _currentApplicationData.Name); IsLoadingIndeterminate = true; CacheLoadStatus = string.Empty; break; @@ -1204,12 +1263,12 @@ namespace Ryujinx.Ava.UI.ViewModels _rendererWaitEvent.Set(); } - private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey, LoadContentFromFolderDelegate onDirsSelected, LocaleKeys dirSelectDialogTitle) + private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey, + LoadContentFromFolderDelegate onDirsSelected, LocaleKeys dirSelectDialogTitle) { - Optional> result = await StorageProvider.OpenMultiFolderPickerAsync(new FolderPickerOpenOptions - { - Title = LocaleManager.Instance[dirSelectDialogTitle] - }); + Optional> result = + await StorageProvider.OpenMultiFolderPickerAsync( + new FolderPickerOpenOptions { Title = LocaleManager.Instance[dirSelectDialogTitle] }); if (result.TryGet(out IReadOnlyList foldersToLoad)) { @@ -1224,7 +1283,8 @@ namespace Ryujinx.Ava.UI.ViewModels await Dispatcher.UIThread.InvokeAsync(async () => { await ContentDialogHelper.ShowTextDialog( - LocaleManager.Instance[numAdded > 0 || numRemoved > 0 ? LocaleKeys.RyujinxConfirm : LocaleKeys.RyujinxInfo], + LocaleManager.Instance[ + numAdded > 0 || numRemoved > 0 ? LocaleKeys.RyujinxConfirm : LocaleKeys.RyujinxInfo], msg, string.Empty, string.Empty, @@ -1253,17 +1313,21 @@ namespace Ryujinx.Ava.UI.ViewModels public void LoadConfigurableHotKeys() { - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI, out Avalonia.Input.Key showUiKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI, + out Avalonia.Input.Key showUiKey)) { ShowUiKey = new KeyGesture(showUiKey); } - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out Avalonia.Input.Key screenshotKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey( + (Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, + out Avalonia.Input.Key screenshotKey)) { ScreenshotKey = new KeyGesture(screenshotKey); } - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out Avalonia.Input.Key pauseKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, + out Avalonia.Input.Key pauseKey)) { PauseKey = new KeyGesture(pauseKey); } @@ -1283,7 +1347,8 @@ namespace Ryujinx.Ava.UI.ViewModels public void SetGridMode() => Glyph = Glyph.Grid; - public void SetAspectRatio(AspectRatio aspectRatio) => ConfigurationState.Instance.Graphics.AspectRatio.Value = aspectRatio; + public void SetAspectRatio(AspectRatio aspectRatio) => + ConfigurationState.Instance.Graphics.AspectRatio.Value = aspectRatio; public async Task InstallFirmwareFromFile() { @@ -1375,7 +1440,8 @@ namespace Ryujinx.Ava.UI.ViewModels } catch (Exception ex) { - Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {screenshotsDir}. Error : {ex.GetType().Name}", "Screenshot"); + Logger.Error?.Print(LogClass.Application, + $"Failed to create directory at path {screenshotsDir}. Error : {ex.GetType().Name}", "Screenshot"); return; } @@ -1448,7 +1514,8 @@ namespace Ryujinx.Ava.UI.ViewModels public async Task ManageProfiles() { - await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient); + await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, + LibHacHorizonManager.RyujinxClient); } public void SimulateWakeUpMessage() @@ -1525,7 +1592,8 @@ namespace Ryujinx.Ava.UI.ViewModels } else { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.MenuBarFileOpenFromFileError]); + await ContentDialogHelper.CreateErrorDialog( + LocaleManager.Instance[LocaleKeys.MenuBarFileOpenFromFileError]); } } } @@ -1550,17 +1618,17 @@ namespace Ryujinx.Ava.UI.ViewModels public async Task OpenFolder() { - Optional result = await StorageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions - { - Title = LocaleManager.Instance[LocaleKeys.LoadUnpackedGameFromFolderDialogTitle] - }); + Optional result = await StorageProvider.OpenSingleFolderPickerAsync( + new FolderPickerOpenOptions + { + Title = LocaleManager.Instance[LocaleKeys.LoadUnpackedGameFromFolderDialogTitle] + }); if (result.TryGet(out IStorageFolder value)) { ApplicationData applicationData = new() { - Name = Path.GetFileNameWithoutExtension(value.Path.LocalPath), - Path = value.Path.LocalPath, + Name = Path.GetFileNameWithoutExtension(value.Path.LocalPath), Path = value.Path.LocalPath, }; await LoadApplication(applicationData); @@ -1570,29 +1638,34 @@ namespace Ryujinx.Ava.UI.ViewModels public static bool InitializeUserConfig(ApplicationData application) { // Code where conditions will be met before loading the user configuration (Global Config) - string backendThreadingInit = Program.BackendThreadingArg ?? ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString(); + string backendThreadingInit = Program.BackendThreadingArg ?? + ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString(); // If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting. string idGame = application.IdBaseString; - if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat)) + if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), + out ConfigurationFileFormat configurationFileFormat)) { // Loads the user configuration, having previously changed the global configuration to the user configuration - ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true), idGame); + ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true), + idGame); - if (ConfigurationFileFormat.TryLoad(Program.GlobalConfigurationPath, out ConfigurationFileFormat configurationFileFormatExtra)) + if (ConfigurationFileFormat.TryLoad(Program.GlobalConfigurationPath, + out ConfigurationFileFormat configurationFileFormatExtra)) { //This is where the global configuration will be stored. //This allows you to change the global configuration settings during the game (for example, the global input setting) - ConfigurationState.InstanceExtra.Load(configurationFileFormatExtra, Program.GlobalConfigurationPath); + ConfigurationState.InstanceExtra.Load(configurationFileFormatExtra, + Program.GlobalConfigurationPath); } } // Code where conditions will be executed after loading user configuration if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != backendThreadingInit) { - Rebooter.RebootAppWithGame(application.Path, + Rebooter.RebootAppWithGame(application.Path, [ - "--bt", + "--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() ]); @@ -1602,7 +1675,8 @@ namespace Ryujinx.Ava.UI.ViewModels return false; } - public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct? customNacpData = null) + public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, + BlitStruct? customNacpData = null) { if (InitializeUserConfig(application)) return; @@ -1626,7 +1700,8 @@ namespace Ryujinx.Ava.UI.ViewModels Logger.RestartTime(); - SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id); + SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, + ConfigurationState.Instance.System.Language, application.Id); PrepareLoadScreen(); @@ -1664,7 +1739,6 @@ namespace Ryujinx.Ava.UI.ViewModels Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" }; gameThread.Start(); - } public void SwitchToRenderer(bool startFullscreen) => @@ -1696,7 +1770,8 @@ namespace Ryujinx.Ava.UI.ViewModels if (version != null) { - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, version.VersionString); + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, + version.VersionString); hasApplet = version.Major > 3; } @@ -1749,7 +1824,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (AppHost.Device.System.SearchingForAmiibo(out int deviceId) && IsGameRunning) { string titleId = AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper(); - AmiiboWindow window = new(ShowAll, LastScannedAmiiboId, titleId); + AmiiboWindow window = new(ShowAll, LastScannedAmiiboId ?? string.Empty, titleId); await StyleableAppWindow.ShowAsync(window); @@ -1762,22 +1837,24 @@ namespace Ryujinx.Ava.UI.ViewModels } } } + public async Task OpenBinFile() { if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning) { - Optional result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions - { - Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle], - FileTypeFilter = new List + Optional result = await StorageProvider.OpenSingleFilePickerAsync( + new FilePickerOpenOptions { - new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats]) + Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle], + FileTypeFilter = new List { - Patterns = ["*.bin"], + new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats]) + { + Patterns = ["*.bin"], + } } - } - }); - + }); + if (result.HasValue) { AppHost.Device.System.ScanAmiiboFromBin(result.Value.Path.LocalPath); @@ -1828,9 +1905,11 @@ namespace Ryujinx.Ava.UI.ViewModels if (ConfigurationState.Instance.Logger.EnableTrace.Value) { string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledMessage]; - string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledConfirmMessage]; + string secondaryMessage = + LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledConfirmMessage]; - UserResult result = await ContentDialogHelper.CreateLocalizedConfirmationDialog(mainMessage, secondaryMessage); + UserResult result = + await ContentDialogHelper.CreateLocalizedConfirmationDialog(mainMessage, secondaryMessage); if (result == UserResult.Yes) { @@ -1843,9 +1922,11 @@ namespace Ryujinx.Ava.UI.ViewModels if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value)) { string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledMessage]; - string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledConfirmMessage]; + string secondaryMessage = + LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledConfirmMessage]; - UserResult result = await ContentDialogHelper.CreateLocalizedConfirmationDialog(mainMessage, secondaryMessage); + UserResult result = + await ContentDialogHelper.CreateLocalizedConfirmationDialog(mainMessage, secondaryMessage); if (result == UserResult.Yes) { @@ -1900,7 +1981,7 @@ namespace Ryujinx.Ava.UI.ViewModels public async void ProcessTrimResult(String filename, XCIFileTrimmer.OperationOutcome operationOutcome) { - string notifyUser = operationOutcome.ToLocalisedText(); + string notifyUser = operationOutcome.LocalizedText; if (notifyUser != null) { @@ -1934,7 +2015,8 @@ namespace Ryujinx.Ava.UI.ViewModels double savings = (double)trimmer.DiskSpaceSavingsB / 1024.0 / 1024.0; double currentFileSize = (double)trimmer.FileSizeB / 1024.0 / 1024.0; double cartDataSize = (double)trimmer.DataSizeB / 1024.0 / 1024.0; - string secondaryText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TrimXCIFileDialogSecondaryText, currentFileSize, cartDataSize, savings); + string secondaryText = LocaleManager.Instance.UpdateAndGetDynamicValue( + LocaleKeys.TrimXCIFileDialogSecondaryText, currentFileSize, cartDataSize, savings); UserResult result = await ContentDialogHelper.CreateConfirmationDialog( LocaleManager.Instance[LocaleKeys.TrimXCIFileDialogPrimaryText], @@ -1950,7 +2032,9 @@ namespace Ryujinx.Ava.UI.ViewModels { Dispatcher.UIThread.Post(() => { - StatusBarProgressStatusText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarXCIFileTrimming, Path.GetFileName(filename)); + StatusBarProgressStatusText = + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarXCIFileTrimming, + Path.GetFileName(filename)); StatusBarProgressStatusVisible = true; StatusBarProgressMaximum = 1; StatusBarProgressValue = 0; @@ -1975,11 +2059,7 @@ namespace Ryujinx.Ava.UI.ViewModels StatusBarVisible = false; }); } - }) - { - Name = "GUI.XCIFileTrimmerThread", - IsBackground = true, - }; + }) { Name = "GUI.XCIFileTrimmerThread", IsBackground = true, }; XCIFileTrimThread.Start(); } } @@ -2041,7 +2121,8 @@ namespace Ryujinx.Ava.UI.ViewModels public static RelayCommand OpenUserSaveDirectory { get; } = Commands.CreateConditional(vm => vm?.SelectedApplication != null, viewModel => - OpenSaveDirectory(viewModel, SaveDataType.Account, viewModel.AccountManager.LastOpenedUser.UserId.ToLibHac()) + OpenSaveDirectory(viewModel, SaveDataType.Account, + viewModel.AccountManager.LastOpenedUser.UserId.ToLibHac()) ); public static RelayCommand OpenDeviceSaveDirectory { get; } = @@ -2097,7 +2178,8 @@ namespace Ryujinx.Ava.UI.ViewModels viewModel => { string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, viewModel.SelectedApplication.IdString); + string titleModsPath = + ModLoader.GetApplicationDir(modsBasePath, viewModel.SelectedApplication.IdString); OpenHelper.OpenFolder(titleModsPath); }); @@ -2107,7 +2189,8 @@ namespace Ryujinx.Ava.UI.ViewModels viewModel => { string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, viewModel.SelectedApplication.IdString); + string titleModsPath = + ModLoader.GetApplicationDir(sdModsBasePath, viewModel.SelectedApplication.IdString); OpenHelper.OpenFolder(titleModsPath); }); diff --git a/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs index abfe5a4c5..45e67add0 100644 --- a/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs @@ -24,9 +24,11 @@ namespace Ryujinx.Ava.UI.ViewModels { private readonly string _modJsonPath; - private AvaloniaList _mods = []; - [ObservableProperty] private AvaloniaList _views = []; - [ObservableProperty] private AvaloniaList _selectedMods = []; + [ObservableProperty] + public partial AvaloniaList Views { get; set; } = []; + + [ObservableProperty] + public partial AvaloniaList SelectedMods { get; set; } = []; private string _search; private readonly ulong _applicationId; @@ -37,15 +39,15 @@ namespace Ryujinx.Ava.UI.ViewModels public AvaloniaList Mods { - get => _mods; + get; set { - _mods = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(ModCount)); Sort(); } - } + } = []; public string Search { @@ -58,10 +60,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public string ModCount - { - get => string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], Mods.Count); - } + public string ModCount => string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], Mods.Count); public ModManagerViewModel(ulong applicationId, ulong applicationIdBase, ApplicationLibrary appLibrary) { diff --git a/src/Ryujinx/UI/ViewModels/ProfileSelectorDialogViewModel.cs b/src/Ryujinx/UI/ViewModels/ProfileSelectorDialogViewModel.cs index 979e1616a..0aa326b55 100644 --- a/src/Ryujinx/UI/ViewModels/ProfileSelectorDialogViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/ProfileSelectorDialogViewModel.cs @@ -7,8 +7,10 @@ namespace Ryujinx.Ava.UI.ViewModels public partial class ProfileSelectorDialogViewModel : BaseModel { - [ObservableProperty] private UserId _selectedUserId; + [ObservableProperty] + public partial UserId SelectedUserId { get; set; } - [ObservableProperty] private ObservableCollection _profiles = []; + [ObservableProperty] + public partial ObservableCollection Profiles { get; set; } = []; } } diff --git a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs index 230887e34..65e91f41f 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs @@ -15,9 +15,13 @@ namespace Ryujinx.Ava.UI.ViewModels _baseViewModel = settingsVm; } - [ObservableProperty] private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; - [ObservableProperty] private bool _nifmDisableIsAnyInternetRequestAccepted = ConfigurationState.Instance.Hacks.DisableNifmIsAnyInternetRequestAccepted; + [ObservableProperty] + public partial bool Xc2MenuSoftlockFix { get; set; } = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; + [ObservableProperty] + public partial bool NifmDisableIsAnyInternetRequestAccepted { get; set; } = ConfigurationState.Instance.Hacks.DisableNifmIsAnyInternetRequestAccepted; + + public static string Xc2MenuFixTooltip { get; } = Lambda.String(sb => { sb.AppendLine( diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index c590e0cb0..894f06969 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -19,7 +19,6 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Helper; -using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; @@ -46,47 +45,49 @@ namespace Ryujinx.Ava.UI.ViewModels private readonly Dictionary _networkInterfaces; - private float _customResolutionScale; private int _resolutionScale; - private int _graphicsBackendMultithreadingIndex; - private float _volume; - [ObservableProperty] private bool _isVulkanAvailable = true; - [ObservableProperty] private bool _gameListNeedsRefresh; + [ObservableProperty] + public partial bool IsVulkanAvailable { get; set; } = true; + + [ObservableProperty] + public partial bool GameListNeedsRefresh { get; set; } + private readonly List _gpuIds = []; - public bool _useInputGlobalConfig; - private int _graphicsBackendIndex; private int _scalingFilter; - private int _scalingFilterLevel; private int _customVSyncInterval; - private bool _enableCustomVSyncInterval; private int _customVSyncIntervalPercentageProxy; private VSyncMode _vSyncMode; - private long _turboModeMultiplier; public event Action CloseWindow; public event Action SaveSettingsEvent; public event Action LocalGlobalInputSwitchEvent; - private int _networkInterfaceIndex; - private int _multiplayerModeIndex; - private string _ldnPassphrase; - [ObservableProperty] private string _ldnServer; - - private bool _enableGDBStub; - private ushort _gdbStubPort; - private bool _debuggerSuspendOnStart; - public SettingsHacksViewModel DirtyHacks { get; } - private readonly bool _isGameRunning; - private readonly Bitmap _gameIcon; - private readonly string _gameTitle; - private readonly string _gamePath; - private readonly string _gameId; - public bool IsGameRunning => _isGameRunning; - public Bitmap GameIcon => _gameIcon; - public string GamePath => _gamePath; - public string GameTitle => _gameTitle; - public string GameId => _gameId; + public bool IsGameRunning + { + get; + } + + public Bitmap GameIcon + { + get; + } + + public string GamePath + { + get; + } + + public string GameTitle + { + get; + } + + public string GameId + { + get; + } + public bool IsGameTitleNotNull => !string.IsNullOrEmpty(GameTitle); public double PanelOpacity => IsGameTitleNotNull ? 0.5 : 1; @@ -104,17 +105,18 @@ namespace Ryujinx.Ava.UI.ViewModels public int GraphicsBackendMultithreadingIndex { - get => _graphicsBackendMultithreadingIndex; + get; set { - _graphicsBackendMultithreadingIndex = value; + field = value; - if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value) + if (field != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value) { Dispatcher.UIThread.InvokeAsync(() => - ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage], - string.Empty, - string.Empty, + ContentDialogHelper.CreateInfoDialog( + LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage], + string.Empty, + string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]) ); @@ -126,10 +128,10 @@ namespace Ryujinx.Ava.UI.ViewModels public float CustomResolutionScale { - get => _customResolutionScale; + get; set { - _customResolutionScale = MathF.Round(value, 1); + field = MathF.Round(value, 1); OnPropertyChanged(); } @@ -152,12 +154,12 @@ namespace Ryujinx.Ava.UI.ViewModels public int FocusLostActionType { get; set; } public bool UseGlobalInputConfig - { - get => _useInputGlobalConfig; + { + get; set { - _useInputGlobalConfig = value; - LocalGlobalInputSwitchEvent?.Invoke(_useInputGlobalConfig); + field = value; + LocalGlobalInputSwitchEvent?.Invoke(field); OnPropertyChanged(nameof(InputPanelOpacity)); OnPropertyChanged(); } @@ -196,10 +198,10 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableCustomVSyncInterval { - get => _enableCustomVSyncInterval; + get; set { - _enableCustomVSyncInterval = value; + field = value; if (_vSyncMode == VSyncMode.Custom && !value) { VSyncMode = VSyncMode.Switch; @@ -233,12 +235,12 @@ namespace Ryujinx.Ava.UI.ViewModels public long TurboMultiplier { - get => _turboModeMultiplier; + get; set { - if (_turboModeMultiplier != value) + if (field != value) { - _turboModeMultiplier = value; + field = value; OnPropertyChanged(); OnPropertyChanged((nameof(TurboMultiplierPercentageText))); @@ -285,10 +287,10 @@ namespace Ryujinx.Ava.UI.ViewModels public string LdnPassphrase { - get => _ldnPassphrase; + get; set { - _ldnPassphrase = value; + field = value; IsInvalidLdnPassphraseVisible = !ValidateLdnPassphrase(value); OnPropertyChanged(); @@ -304,29 +306,33 @@ namespace Ryujinx.Ava.UI.ViewModels public int AspectRatio { get; set; } public int AntiAliasingEffect { get; set; } public string ScalingFilterLevelText => ScalingFilterLevel.ToString("0"); + public int ScalingFilterLevel { - get => _scalingFilterLevel; + get; set { - _scalingFilterLevel = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(ScalingFilterLevelText)); } } + public int OpenglDebugLevel { get; set; } public int MemoryMode { get; set; } public int BaseStyleIndex { get; set; } + public int GraphicsBackendIndex { - get => _graphicsBackendIndex; + get; set { - _graphicsBackendIndex = value; + field = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsVulkanSelected)); } } + public int ScalingFilter { get => _scalingFilter; @@ -342,12 +348,12 @@ namespace Ryujinx.Ava.UI.ViewModels public float Volume { - get => _volume; + get; set { - _volume = value; + field = value; - ConfigurationState.Instance.System.AudioVolume.Value = _volume / 100; + ConfigurationState.Instance.System.AudioVolume.Value = field / 100; OnPropertyChanged(); } @@ -373,19 +379,19 @@ namespace Ryujinx.Ava.UI.ViewModels public int NetworkInterfaceIndex { - get => _networkInterfaceIndex; + get; set { - _networkInterfaceIndex = value != -1 ? value : 0; + field = value != -1 ? value : 0; } } public int MultiplayerModeIndex { - get => _multiplayerModeIndex; + get; set { - _multiplayerModeIndex = value; + field = value; } } @@ -393,31 +399,31 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableGdbStub { - get => _enableGDBStub; + get; set { - _enableGDBStub = value; - ConfigurationState.Instance.Debug.EnableGdbStub.Value = _enableGDBStub; + field = value; + ConfigurationState.Instance.Debug.EnableGdbStub.Value = field; } } public ushort GDBStubPort { - get => _gdbStubPort; + get; set { - _gdbStubPort = value; - ConfigurationState.Instance.Debug.GdbStubPort.Value = _gdbStubPort; + field = value; + ConfigurationState.Instance.Debug.GdbStubPort.Value = field; } } public bool DebuggerSuspendOnStart { - get => _debuggerSuspendOnStart; + get; set { - _debuggerSuspendOnStart = value; - ConfigurationState.Instance.Debug.DebuggerSuspendOnStart.Value = _debuggerSuspendOnStart; + field = value; + ConfigurationState.Instance.Debug.DebuggerSuspendOnStart.Value = field; } } @@ -450,13 +456,13 @@ namespace Ryujinx.Ava.UI.ViewModels if (gameIconData is { Length: > 0 }) { using MemoryStream ms = new(gameIconData); - _gameIcon = new Bitmap(ms); + GameIcon = new Bitmap(ms); } - _isGameRunning = gameRunning; - _gamePath = gamePath; - _gameTitle = gameName; - _gameId = gameId; + IsGameRunning = gameRunning; + GamePath = gamePath; + GameTitle = gameName; + GameId = gameId; if (customConfig) // During the game. If there is no user config, then load the global config window { @@ -717,7 +723,6 @@ namespace Ryujinx.Ava.UI.ViewModels MultiplayerModeIndex = (int)config.Multiplayer.Mode.Value; DisableP2P = config.Multiplayer.DisableP2p; LdnPassphrase = config.Multiplayer.LdnPassphrase; - LdnServer = config.Multiplayer.LdnServer; // Debug EnableGdbStub = config.Debug.EnableGdbStub.Value; @@ -842,7 +847,6 @@ namespace Ryujinx.Ava.UI.ViewModels config.Multiplayer.Mode.Value = (MultiplayerMode)MultiplayerModeIndex; config.Multiplayer.DisableP2p.Value = DisableP2P; config.Multiplayer.LdnPassphrase.Value = LdnPassphrase; - config.Multiplayer.LdnServer.Value = LdnServer; // Debug config.Debug.EnableGdbStub.Value = EnableGdbStub; diff --git a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs index 74876774e..3d34643ab 100644 --- a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs @@ -21,10 +21,17 @@ namespace Ryujinx.Ava.UI.ViewModels private ApplicationLibrary ApplicationLibrary { get; } private ApplicationData ApplicationData { get; } - [ObservableProperty] private AvaloniaList _titleUpdates = []; - [ObservableProperty] private AvaloniaList _views = []; - [ObservableProperty] private object _selectedUpdate = new TitleUpdateViewModelNoUpdate(); - [ObservableProperty] private bool _showBundledContentNotice; + [ObservableProperty] + public partial AvaloniaList TitleUpdates { get; set; } = []; + + [ObservableProperty] + public partial AvaloniaList Views { get; set; } = []; + + [ObservableProperty] + public partial object SelectedUpdate { get; set; } = new TitleUpdateViewModelNoUpdate(); + + [ObservableProperty] + public partial bool ShowBundledContentNotice { get; set; } private readonly IStorageProvider _storageProvider; diff --git a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs index 8c6a71038..9b1c75b2e 100644 --- a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs @@ -25,14 +25,15 @@ namespace Ryujinx.Ava.UI.ViewModels { private static readonly Dictionary _avatarStore = new(); - [ObservableProperty] private ObservableCollection _images; - [ObservableProperty] private Color _backgroundColor = Colors.White; + [ObservableProperty] + public partial ObservableCollection Images { get; set; } - private int _selectedIndex; + [ObservableProperty] + public partial Color BackgroundColor { get; set; } = Colors.White; public UserFirmwareAvatarSelectorViewModel() { - _images = []; + Images = []; LoadImagesFromStore(); PropertyChanged += (_, args) => @@ -44,21 +45,17 @@ namespace Ryujinx.Ava.UI.ViewModels public int SelectedIndex { - get => _selectedIndex; + get; set { - _selectedIndex = value; + field = value; - if (_selectedIndex == -1) - { - SelectedImage = null; - } - else - { - SelectedImage = Images[_selectedIndex].Data; - } + SelectedImage = field == -1 + ? null + : Images[field].Data; OnPropertyChanged(); + OnPropertyChanged(nameof(SelectedImage)); } } diff --git a/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs index f9f9ca2f5..5a6af229d 100644 --- a/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs @@ -4,6 +4,7 @@ namespace Ryujinx.Ava.UI.ViewModels { public partial class UserProfileImageSelectorViewModel : BaseModel { - [ObservableProperty] private bool _firmwareFound; + [ObservableProperty] + public partial bool FirmwareFound { get; set; } } } diff --git a/src/Ryujinx/UI/ViewModels/UserSaveManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/UserSaveManagerViewModel.cs index eb7fa0fef..34c83372a 100644 --- a/src/Ryujinx/UI/ViewModels/UserSaveManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserSaveManagerViewModel.cs @@ -10,11 +10,21 @@ namespace Ryujinx.Ava.UI.ViewModels { public partial class UserSaveManagerViewModel : BaseModel { - [ObservableProperty] private int _sortIndex; - [ObservableProperty] private int _orderIndex; - [ObservableProperty] private string _search; - [ObservableProperty] private ObservableCollection _saves = []; - [ObservableProperty] private ObservableCollection _views = []; + [ObservableProperty] + public partial int SortIndex { get; set; } + + [ObservableProperty] + public partial int OrderIndex { get; set; } + + [ObservableProperty] + public partial string Search { get; set; } + + [ObservableProperty] + public partial ObservableCollection Saves { get; set; } = []; + + [ObservableProperty] + public partial ObservableCollection Views { get; set; } = []; + private readonly AccountManager _accountManager; public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId); diff --git a/src/Ryujinx/UI/ViewModels/XciTrimmerViewModel.cs b/src/Ryujinx/UI/ViewModels/XciTrimmerViewModel.cs index dab42fbf8..1281cc834 100644 --- a/src/Ryujinx/UI/ViewModels/XciTrimmerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/XciTrimmerViewModel.cs @@ -18,7 +18,8 @@ namespace Ryujinx.Ava.UI.ViewModels { public class XciTrimmerViewModel : BaseModel { - private const long _bytesPerMB = 1024 * 1024; + private const long BytesPerMb = 1024 * 1024; + private enum ProcessingMode { Trimming, @@ -44,7 +45,6 @@ namespace Ryujinx.Ava.UI.ViewModels private string _search; private ProcessingMode _processingMode; private SortField _sortField = SortField.Name; - private bool _sortAscending = true; public XciTrimmerViewModel(MainWindowViewModel mainWindowViewModel) { @@ -472,15 +472,16 @@ namespace Ryujinx.Ava.UI.ViewModels }; } } + public bool SortingAscending { - get => _sortAscending; + get; set { - _sortAscending = value; + field = value; SortingChanged(); } - } + } = true; public bool IsSortedByName { @@ -516,7 +517,7 @@ namespace Ryujinx.Ava.UI.ViewModels { get { - return string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerSavingsMb], AllXCIFiles.Sum(xci => xci.PotentialSavingsB / _bytesPerMB)); + return string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerSavingsMb], AllXCIFiles.Sum(xci => xci.PotentialSavingsB / BytesPerMb)); } } @@ -524,7 +525,7 @@ namespace Ryujinx.Ava.UI.ViewModels { get { - return string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerSavingsMb], AllXCIFiles.Sum(xci => xci.CurrentSavingsB / _bytesPerMB)); + return string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerSavingsMb], AllXCIFiles.Sum(xci => xci.CurrentSavingsB / BytesPerMb)); } } diff --git a/src/Ryujinx/UI/Views/Dialog/AboutView.axaml b/src/Ryujinx/UI/Views/Dialog/AboutView.axaml index 5788f533f..5700de6b9 100644 --- a/src/Ryujinx/UI/Views/Dialog/AboutView.axaml +++ b/src/Ryujinx/UI/Views/Dialog/AboutView.axaml @@ -6,6 +6,7 @@ xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels" + xmlns:interopServices="clr-namespace:System.Runtime.InteropServices;assembly=System.Runtime" MinWidth="550" MinHeight="260" MaxWidth="600" @@ -70,6 +71,13 @@ LineHeight="12" Text="{Binding Version}" TextAlignment="Center" /> + > OpenSingleFolderPickerAsync(this IStorageProvider storageProvider, FolderPickerOpenOptions openOptions = null) => - await storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, false)) - .Then(folders => folders.FindFirst()); - - public static async Task> OpenSingleFilePickerAsync(this IStorageProvider storageProvider, FilePickerOpenOptions openOptions = null) => - await storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, false)) - .Then(files => files.FindFirst()); - - public static async Task>> OpenMultiFolderPickerAsync(this IStorageProvider storageProvider, FolderPickerOpenOptions openOptions = null) => - await storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, true)) - .Then(folders => folders.Count > 0 ? Optional.Of(folders) : default); - - public static async Task>> OpenMultiFilePickerAsync(this IStorageProvider storageProvider, FilePickerOpenOptions openOptions = null) => - await storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, true)) - .Then(files => files.Count > 0 ? Optional.Of(files) : default); + extension(IStorageProvider storageProvider) + { + public Task> OpenSingleFolderPickerAsync(FolderPickerOpenOptions openOptions = null) => + storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, false)) + .Then(folders => folders.FindFirst()); + + public Task> OpenSingleFilePickerAsync(FilePickerOpenOptions openOptions = null) => + storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, false)) + .Then(files => files.FindFirst()); + + public Task>> OpenMultiFolderPickerAsync(FolderPickerOpenOptions openOptions = null) => + storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, true)) + .Then(folders => folders.Count > 0 ? Optional.Of(folders) : default); + + public Task>> OpenMultiFilePickerAsync(FilePickerOpenOptions openOptions = null) => + storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, true)) + .Then(files => files.Count > 0 ? Optional.Of(files) : default); + } private static FilePickerOpenOptions FixOpenOptions(this FilePickerOpenOptions openOptions, bool allowMultiple) {