From c2e4c8f98e5d49749f8691fd0982a746339f8211 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 22 May 2023 18:41:08 +0100 Subject: [PATCH 01/25] Bump MVK Version (#5057) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0bbe81406..c46a41bf0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -32,7 +32,7 @@ - + From 2725e40838070f34e2d10938f3a4223ee0629186 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 22 May 2023 17:12:11 -0300 Subject: [PATCH 02/25] Revert "Bump MVK Version (#5057)" (#5061) This reverts commit c2e4c8f98e5d49749f8691fd0982a746339f8211. --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index c46a41bf0..0bbe81406 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -32,7 +32,7 @@ - + From 6cb6b15612a20717c0e98045914b535f582cdc33 Mon Sep 17 00:00:00 2001 From: makigumo Date: Mon, 22 May 2023 22:32:15 +0200 Subject: [PATCH 03/25] Implement p2rc, p2ri, p2rr and r2p.cc shaders (#5031) * implement P2rC, P2rI, P2rR shaders * implement R2p.CC shader * bump CodeGenVersion * address feedback --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Instructions/InstEmit.cs | 21 ------ .../Instructions/InstEmitMove.cs | 22 +++---- .../Instructions/InstEmitPredicate.cs | 64 ++++++++++++++++++- 4 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 9f263e9d7..400f63f56 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4646; + private const uint CodeGenVersion = 5031; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs index 3a9e658aa..963a5c656 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs @@ -187,27 +187,6 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented."); } - public static void P2rR(EmitterContext context) - { - InstP2rR op = context.GetOp(); - - context.Config.GpuAccessor.Log("Shader instruction P2rR is not implemented."); - } - - public static void P2rI(EmitterContext context) - { - InstP2rI op = context.GetOp(); - - context.Config.GpuAccessor.Log("Shader instruction P2rI is not implemented."); - } - - public static void P2rC(EmitterContext context) - { - InstP2rC op = context.GetOp(); - - context.Config.GpuAccessor.Log("Shader instruction P2rC is not implemented."); - } - public static void Pexit(EmitterContext context) { InstPexit op = context.GetOp(); diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index 9992ac378..e12177f7d 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -209,21 +209,15 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.ICompareNotEqual(context.BitwiseAnd(value, Const(1 << bit)), Const(0)); } - if (ccpr) + int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount; + RegisterType type = ccpr ? RegisterType.Flag : RegisterType.Predicate; + int shift = (int)byteSel * 8; + + for (int bit = 0; bit < count; bit++) { - // TODO: Support Register to condition code flags copy. - context.Config.GpuAccessor.Log("R2P.CC not implemented."); - } - else - { - int shift = (int)byteSel * 8; - - for (int bit = 0; bit < RegisterConsts.PredsCount; bit++) - { - Operand pred = Register(bit, RegisterType.Predicate); - Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), pred); - context.Copy(pred, res); - } + Operand flag = Register(bit, type); + Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), flag); + context.Copy(flag, res); } } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs index d605661ff..79919624e 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -50,5 +49,68 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res); context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res); } + + public static void P2rC(EmitterContext context) + { + InstP2rC op = context.GetOp(); + + Operand srcA = GetSrcReg(context, op.SrcA); + Operand dest = GetSrcReg(context, op.Dest); + Operand mask = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); + + EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr); + } + + public static void P2rI(EmitterContext context) + { + InstP2rI op = context.GetOp(); + + Operand srcA = GetSrcReg(context, op.SrcA); + Operand dest = GetSrcReg(context, op.Dest); + Operand mask = GetSrcImm(context, op.Imm20); + + EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr); + } + + public static void P2rR(EmitterContext context) + { + InstP2rR op = context.GetOp(); + + Operand srcA = GetSrcReg(context, op.SrcA); + Operand dest = GetSrcReg(context, op.Dest); + Operand mask = GetSrcReg(context, op.SrcB); + + EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr); + } + + private static void EmitP2r( + EmitterContext context, + Operand srcA, + Operand dest, + Operand mask, + ByteSel byteSel, + bool ccpr) + { + int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount; + int shift = (int)byteSel * 8; + mask = context.BitwiseAnd(mask, Const(0xff)); + + Operand insert = Const(0); + for (int i = 0; i < count; i++) + { + Operand condition = ccpr + ? Register(i, RegisterType.Flag) + : Register(i, RegisterType.Predicate); + + Operand bit = context.ConditionalSelect(condition, Const(1 << (i + shift)), Const(0)); + insert = context.BitwiseOr(insert, bit); + } + + Operand maskShifted = context.ShiftLeft(mask, Const(shift)); + Operand masked = context.BitwiseAnd(srcA, context.BitwiseNot(maskShifted)); + Operand res = context.BitwiseOr(masked, context.BitwiseAnd(insert, maskShifted)); + + context.Copy(dest, res); + } } } \ No newline at end of file From 4ca78eded52f21089400cc28351b9353279b8171 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 23 May 2023 05:41:37 -0300 Subject: [PATCH 04/25] Vulkan: Do not set storage flag for multisample textures if not supported (#5060) --- src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs | 3 +++ src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 6 +++--- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 2 +- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 5cab4113a..4b9ace3aa 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsSubgroupSizeControl; public readonly bool SupportsShaderInt8; public readonly bool SupportsShaderStencilExport; + public readonly bool SupportsShaderStorageImageMultisample; public readonly bool SupportsConditionalRendering; public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsMultiView; @@ -63,6 +64,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsSubgroupSizeControl, bool supportsShaderInt8, bool supportsShaderStencilExport, + bool supportsShaderStorageImageMultisample, bool supportsConditionalRendering, bool supportsExtendedDynamicState, bool supportsMultiView, @@ -97,6 +99,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsSubgroupSizeControl = supportsSubgroupSizeControl; SupportsShaderInt8 = supportsShaderInt8; SupportsShaderStencilExport = supportsShaderStencilExport; + SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample; SupportsConditionalRendering = supportsConditionalRendering; SupportsExtendedDynamicState = supportsExtendedDynamicState; SupportsMultiView = supportsMultiView; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 0582e6ca8..4edb6f2fe 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); - var usage = GetImageUsageFromFormat(info.Format); + var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); var flags = ImageCreateFlags.CreateMutableFormatBit; @@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public static ImageUsageFlags GetImageUsageFromFormat(GAL.Format format) + public static ImageUsageFlags GetImageUsage(GAL.Format format, Target target, bool supportsMsStorage) { var usage = DefaultUsageFlags; @@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Vulkan usage |= ImageUsageFlags.ColorAttachmentBit; } - if (format.IsImageCompatible()) + if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample())) { usage |= ImageUsageFlags.StorageBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index 62d481eb9..c2be74974 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Textures.Add(this); var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); - var usage = TextureStorage.GetImageUsageFromFormat(info.Format); + var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); var levels = (uint)info.Levels; var layers = (uint)info.GetLayers(); diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 74267325c..4f3f72345 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -295,6 +295,7 @@ namespace Ryujinx.Graphics.Vulkan supportsSubgroupSizeControl, featuresShaderInt8.ShaderInt8, _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"), + features2.Features.ShaderStorageImageMultisample, _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), features2.Features.MultiViewport, From 274af65f69f25c88c50b626b785a33aade9a1b63 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Thu, 25 May 2023 15:17:37 +0100 Subject: [PATCH 05/25] Update release.yml (#5058) --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a32033190..491fe6d9e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,7 @@ jobs: shell: bash - name: Create tag - uses: actions/github-script@v5 + uses: actions/github-script@v6 with: script: | github.rest.git.createRef({ @@ -207,4 +207,4 @@ jobs: needs: release with: ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit \ No newline at end of file + secrets: inherit From 2c9715acf6cb9ae8bceabec5e81602c7d64c7192 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 25 May 2023 17:03:51 -0300 Subject: [PATCH 06/25] Truncate vertex attribute format if it exceeds stride on MoltenVK (#5094) * Truncate vertex attribute format if it exceeds stride on MoltenVK * Fix BGR format * Move vertex attribute check to pipeline creation to avoid costs * No need for this to be public --- src/Ryujinx.Graphics.Vulkan/FormatTable.cs | 218 +++++++++++++++++++ src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 49 ++++- 2 files changed, 265 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs index 45fc46ada..a030d8c85 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs @@ -168,5 +168,223 @@ namespace Ryujinx.Graphics.Vulkan { return _table[(int)format]; } + + public static int GetAttributeFormatSize(VkFormat format) + { + switch (format) + { + case VkFormat.R8Unorm: + case VkFormat.R8SNorm: + case VkFormat.R8Uint: + case VkFormat.R8Sint: + case VkFormat.R8Uscaled: + case VkFormat.R8Sscaled: + return 1; + + case VkFormat.R8G8Unorm: + case VkFormat.R8G8SNorm: + case VkFormat.R8G8Uint: + case VkFormat.R8G8Sint: + case VkFormat.R8G8Uscaled: + case VkFormat.R8G8Sscaled: + case VkFormat.R16Sfloat: + case VkFormat.R16Unorm: + case VkFormat.R16SNorm: + case VkFormat.R16Uint: + case VkFormat.R16Sint: + case VkFormat.R16Uscaled: + case VkFormat.R16Sscaled: + return 2; + + case VkFormat.R8G8B8Unorm: + case VkFormat.R8G8B8SNorm: + case VkFormat.R8G8B8Uint: + case VkFormat.R8G8B8Sint: + case VkFormat.R8G8B8Uscaled: + case VkFormat.R8G8B8Sscaled: + return 3; + + case VkFormat.R8G8B8A8Unorm: + case VkFormat.R8G8B8A8SNorm: + case VkFormat.R8G8B8A8Uint: + case VkFormat.R8G8B8A8Sint: + case VkFormat.R8G8B8A8Srgb: + case VkFormat.R8G8B8A8Uscaled: + case VkFormat.R8G8B8A8Sscaled: + case VkFormat.B8G8R8A8Unorm: + case VkFormat.B8G8R8A8Srgb: + case VkFormat.R16G16Sfloat: + case VkFormat.R16G16Unorm: + case VkFormat.R16G16SNorm: + case VkFormat.R16G16Uint: + case VkFormat.R16G16Sint: + case VkFormat.R16G16Uscaled: + case VkFormat.R16G16Sscaled: + case VkFormat.R32Sfloat: + case VkFormat.R32Uint: + case VkFormat.R32Sint: + case VkFormat.A2B10G10R10UnormPack32: + case VkFormat.A2B10G10R10UintPack32: + case VkFormat.B10G11R11UfloatPack32: + case VkFormat.E5B9G9R9UfloatPack32: + case VkFormat.A2B10G10R10SNormPack32: + case VkFormat.A2B10G10R10SintPack32: + case VkFormat.A2B10G10R10UscaledPack32: + case VkFormat.A2B10G10R10SscaledPack32: + return 4; + + case VkFormat.R16G16B16Sfloat: + case VkFormat.R16G16B16Unorm: + case VkFormat.R16G16B16SNorm: + case VkFormat.R16G16B16Uint: + case VkFormat.R16G16B16Sint: + case VkFormat.R16G16B16Uscaled: + case VkFormat.R16G16B16Sscaled: + return 6; + + case VkFormat.R16G16B16A16Sfloat: + case VkFormat.R16G16B16A16Unorm: + case VkFormat.R16G16B16A16SNorm: + case VkFormat.R16G16B16A16Uint: + case VkFormat.R16G16B16A16Sint: + case VkFormat.R16G16B16A16Uscaled: + case VkFormat.R16G16B16A16Sscaled: + case VkFormat.R32G32Sfloat: + case VkFormat.R32G32Uint: + case VkFormat.R32G32Sint: + return 8; + + case VkFormat.R32G32B32Sfloat: + case VkFormat.R32G32B32Uint: + case VkFormat.R32G32B32Sint: + return 12; + + case VkFormat.R32G32B32A32Sfloat: + case VkFormat.R32G32B32A32Uint: + case VkFormat.R32G32B32A32Sint: + return 16; + } + + return 1; + } + + public static VkFormat DropLastComponent(VkFormat format) + { + switch (format) + { + case VkFormat.R8G8Unorm: + return VkFormat.R8Unorm; + case VkFormat.R8G8SNorm: + return VkFormat.R8SNorm; + case VkFormat.R8G8Uint: + return VkFormat.R8Uint; + case VkFormat.R8G8Sint: + return VkFormat.R8Sint; + case VkFormat.R8G8Uscaled: + return VkFormat.R8Uscaled; + case VkFormat.R8G8Sscaled: + return VkFormat.R8Sscaled; + + case VkFormat.R8G8B8Unorm: + return VkFormat.R8G8Unorm; + case VkFormat.R8G8B8SNorm: + return VkFormat.R8G8SNorm; + case VkFormat.R8G8B8Uint: + return VkFormat.R8G8Uint; + case VkFormat.R8G8B8Sint: + return VkFormat.R8G8Sint; + case VkFormat.R8G8B8Uscaled: + return VkFormat.R8G8Uscaled; + case VkFormat.R8G8B8Sscaled: + return VkFormat.R8G8Sscaled; + + case VkFormat.R8G8B8A8Unorm: + return VkFormat.R8G8B8Unorm; + case VkFormat.R8G8B8A8SNorm: + return VkFormat.R8G8B8SNorm; + case VkFormat.R8G8B8A8Uint: + return VkFormat.R8G8B8Uint; + case VkFormat.R8G8B8A8Sint: + return VkFormat.R8G8B8Sint; + case VkFormat.R8G8B8A8Srgb: + return VkFormat.R8G8B8Srgb; + case VkFormat.R8G8B8A8Uscaled: + return VkFormat.R8G8B8Uscaled; + case VkFormat.R8G8B8A8Sscaled: + return VkFormat.R8G8B8Sscaled; + case VkFormat.B8G8R8A8Unorm: + return VkFormat.B8G8R8Unorm; + case VkFormat.B8G8R8A8Srgb: + return VkFormat.B8G8R8Srgb; + + case VkFormat.R16G16Sfloat: + return VkFormat.R16Sfloat; + case VkFormat.R16G16Unorm: + return VkFormat.R16Unorm; + case VkFormat.R16G16SNorm: + return VkFormat.R16SNorm; + case VkFormat.R16G16Uint: + return VkFormat.R16Uint; + case VkFormat.R16G16Sint: + return VkFormat.R16Sint; + case VkFormat.R16G16Uscaled: + return VkFormat.R16Uscaled; + case VkFormat.R16G16Sscaled: + return VkFormat.R16Sscaled; + + case VkFormat.R16G16B16Sfloat: + return VkFormat.R16G16Sfloat; + case VkFormat.R16G16B16Unorm: + return VkFormat.R16G16Unorm; + case VkFormat.R16G16B16SNorm: + return VkFormat.R16G16SNorm; + case VkFormat.R16G16B16Uint: + return VkFormat.R16G16Uint; + case VkFormat.R16G16B16Sint: + return VkFormat.R16G16Sint; + case VkFormat.R16G16B16Uscaled: + return VkFormat.R16G16Uscaled; + case VkFormat.R16G16B16Sscaled: + return VkFormat.R16G16Sscaled; + + case VkFormat.R16G16B16A16Sfloat: + return VkFormat.R16G16B16Sfloat; + case VkFormat.R16G16B16A16Unorm: + return VkFormat.R16G16B16Unorm; + case VkFormat.R16G16B16A16SNorm: + return VkFormat.R16G16B16SNorm; + case VkFormat.R16G16B16A16Uint: + return VkFormat.R16G16B16Uint; + case VkFormat.R16G16B16A16Sint: + return VkFormat.R16G16B16Sint; + case VkFormat.R16G16B16A16Uscaled: + return VkFormat.R16G16B16Uscaled; + case VkFormat.R16G16B16A16Sscaled: + return VkFormat.R16G16B16Sscaled; + + case VkFormat.R32G32Sfloat: + return VkFormat.R32Sfloat; + case VkFormat.R32G32Uint: + return VkFormat.R32Uint; + case VkFormat.R32G32Sint: + return VkFormat.R32Sint; + + case VkFormat.R32G32B32Sfloat: + return VkFormat.R32G32Sfloat; + case VkFormat.R32G32B32Uint: + return VkFormat.R32G32Uint; + case VkFormat.R32G32B32Sint: + return VkFormat.R32G32Sint; + + case VkFormat.R32G32B32A32Sfloat: + return VkFormat.R32G32B32Sfloat; + case VkFormat.R32G32B32A32Uint: + return VkFormat.R32G32B32Uint; + case VkFormat.R32G32B32A32Sint: + return VkFormat.R32G32B32Sint; + } + + return format; + } } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index dccc8ce68..a3d8dd6fb 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -1,4 +1,5 @@ -using Silk.NET.Vulkan; +using Ryujinx.Common.Memory; +using Silk.NET.Vulkan; using System; namespace Ryujinx.Graphics.Vulkan @@ -308,6 +309,8 @@ namespace Ryujinx.Graphics.Vulkan public PipelineLayout PipelineLayout; public SpecData SpecializationData; + private Array32 _vertexAttributeDescriptions2; + public void Initialize() { Stages = new NativeArray(Constants.MaxShaderStages); @@ -400,7 +403,15 @@ namespace Ryujinx.Graphics.Vulkan Pipeline pipelineHandle = default; + bool isMoltenVk = gd.IsMoltenVk; + + if (isMoltenVk) + { + UpdateVertexAttributeDescriptions(); + } + fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0]) + fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0]) fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0]) fixed (Viewport* pViewports = &Internal.Viewports[0]) fixed (Rect2D* pScissors = &Internal.Scissors[0]) @@ -410,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PipelineVertexInputStateCreateInfo, VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount, - PVertexAttributeDescriptions = pVertexAttributeDescriptions, + PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions, VertexBindingDescriptionCount = VertexBindingDescriptionsCount, PVertexBindingDescriptions = pVertexBindingDescriptions }; @@ -612,6 +623,40 @@ namespace Ryujinx.Graphics.Vulkan } } + private void UpdateVertexAttributeDescriptions() + { + // Vertex attributes exceeding the stride are invalid. + // In metal, they cause glitches with the vertex shader fetching incorrect values. + // To work around this, we reduce the format to something that doesn't exceed the stride if possible. + // The assumption is that the exceeding components are not actually accessed on the shader. + + for (int index = 0; index < VertexAttributeDescriptionsCount; index++) + { + var attribute = Internal.VertexAttributeDescriptions[index]; + ref var vb = ref Internal.VertexBindingDescriptions[(int)attribute.Binding]; + + Format format = attribute.Format; + + while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride) + { + Format newFormat = FormatTable.DropLastComponent(format); + + if (newFormat == format) + { + // That case means we failed to find a format that fits within the stride, + // so just restore the original format and give up. + format = attribute.Format; + break; + } + + format = newFormat; + } + + attribute.Format = format; + _vertexAttributeDescriptions2[index] = attribute; + } + } + public void Dispose() { Stages.Dispose(); From 8f0c89ffd65eb2b979b24d457708218050fec6ae Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 25 May 2023 17:46:58 -0300 Subject: [PATCH 07/25] Generate scaling helper functions on IR (#4714) * Generate scaling helper functions on IR * Delete unused code * Split RewriteTextureSample and move gather bias add to an earlier pass * Remove using * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 29 +- .../HelperFunctions/TexelFetchScale_cp.glsl | 19 - .../HelperFunctions/TexelFetchScale_fp.glsl | 26 - .../HelperFunctions/TexelFetchScale_vp.glsl | 20 - .../Glsl/Instructions/InstGenHelper.cs | 1 + .../Glsl/Instructions/InstGenMemory.cs | 84 +--- .../CodeGen/Spirv/CodeGenContext.cs | 1 + .../CodeGen/Spirv/Instructions.cs | 54 +-- .../CodeGen/Spirv/ScalingHelpers.cs | 227 --------- .../CodeGen/Spirv/SpirvDelegates.cs | 2 + .../CodeGen/Spirv/SpirvGenerator.cs | 5 +- .../IntermediateRepresentation/Instruction.cs | 1 + .../Ryujinx.Graphics.Shader.csproj | 7 - .../StructuredIr/AstTextureOperation.cs | 5 - .../StructuredIr/InstructionInfo.cs | 1 + .../StructuredIr/StructuredProgram.cs | 4 +- .../Translation/EmitterContext.cs | 10 +- .../Translation/EmitterContextInsts.cs | 6 +- .../Translation/HelperFunctionManager.cs | 134 ++++++ .../Translation/HelperFunctionName.cs | 11 + .../Translation/Rewriter.cs | 445 +++++++++++++----- .../Translation/ShaderConfig.cs | 30 +- .../Translation/ShaderIdentifier.cs | 8 +- .../Translation/Translator.cs | 12 +- 25 files changed, 560 insertions(+), 584 deletions(-) delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 400f63f56..b8d74a463 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5031; + private const uint CodeGenVersion = 4714; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 8d805e32e..1bd0182b5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -239,33 +239,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - bool isFragment = context.Config.Stage == ShaderStage.Fragment; - - if (isFragment || context.Config.Stage == ShaderStage.Compute || context.Config.Stage == ShaderStage.Vertex) + if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryEarlyZForce()) { - if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce()) - { - context.AppendLine("layout(early_fragment_tests) in;"); - context.AppendLine(); - } - - if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0) - { - string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage); - - int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length; - - if (isFragment) - { - scaleElements++; // Also includes render target scale, for gl_FragCoord. - } - - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0) - { - AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl"); - context.AppendLine(); - } - } + context.AppendLine("layout(early_fragment_tests) in;"); + context.AppendLine(); } if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl deleted file mode 100644 index 08c625488..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl +++ /dev/null @@ -1,19 +0,0 @@ -ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) -{ - float scale = support_buffer.s_render_scale[1 + samplerIndex]; - if (scale == 1.0) - { - return inputVec; - } - return ivec2(vec2(inputVec) * scale); -} - -int Helper_TextureSizeUnscale(int size, int samplerIndex) -{ - float scale = support_buffer.s_render_scale[1 + samplerIndex]; - if (scale == 1.0) - { - return size; - } - return int(float(size) / scale); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl deleted file mode 100644 index 07a38a7a6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl +++ /dev/null @@ -1,26 +0,0 @@ -ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) -{ - float scale = support_buffer.s_render_scale[1 + samplerIndex]; - if (scale == 1.0) - { - return inputVec; - } - if (scale < 0.0) // If less than 0, try interpolate between texels by using the screen position. - { - return ivec2(vec2(inputVec) * (-scale) + mod(gl_FragCoord.xy, 0.0 - scale)); - } - else - { - return ivec2(vec2(inputVec) * scale); - } -} - -int Helper_TextureSizeUnscale(int size, int samplerIndex) -{ - float scale = abs(support_buffer.s_render_scale[1 + samplerIndex]); - if (scale == 1.0) - { - return size; - } - return int(float(size) / scale); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl deleted file mode 100644 index 72baa441d..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl +++ /dev/null @@ -1,20 +0,0 @@ -ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) -{ - float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]); - if (scale == 1.0) - { - return inputVec; - } - - return ivec2(vec2(inputVec) * scale); -} - -int Helper_TextureSizeUnscale(int size, int samplerIndex) -{ - float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]); - if (scale == 1.0) - { - return size; - } - return int(float(size) / scale); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 71e40fe7c..6cf36a2a6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -101,6 +101,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.MemoryBarrier, InstType.CallNullary, "memoryBarrier"); Add(Instruction.Minimum, InstType.CallBinary, "min"); Add(Instruction.MinimumU32, InstType.CallBinary, "min"); + Add(Instruction.Modulo, InstType.CallBinary, "mod"); Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1); Add(Instruction.MultiplyHighS32, InstType.CallBinary, HelperFunctionNames.MultiplyHighS32); Add(Instruction.MultiplyHighU32, InstType.CallBinary, HelperFunctionNames.MultiplyHighU32); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index ef5260d10..dfc8197b6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -97,30 +97,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCallBuilder.Append(str); } - string ApplyScaling(string vector) - { - if (context.Config.Stage.SupportsRenderScale() && - texOp.Inst == Instruction.ImageLoad && - !isBindless && - !isIndexed) - { - // Image scales start after texture ones. - int scaleIndex = context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp); - - if (pCount == 3 && isArray) - { - // The array index is not scaled, just x and y. - vector = $"ivec3(Helper_TexelFetchScale(({vector}).xy, {scaleIndex}), ({vector}).z)"; - } - else if (pCount == 2 && !isArray) - { - vector = $"Helper_TexelFetchScale({vector}, {scaleIndex})"; - } - } - - return vector; - } - if (pCount > 1) { string[] elems = new string[pCount]; @@ -130,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions elems[index] = Src(AggregateType.S32); } - Append(ApplyScaling($"ivec{pCount}({string.Join(", ", elems)})")); + Append($"ivec{pCount}({string.Join(", ", elems)})"); } else { @@ -584,53 +560,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } - string ApplyScaling(string vector) - { - if (intCoords) - { - if (context.Config.Stage.SupportsRenderScale() && - !isBindless && - !isIndexed) - { - int index = context.Config.FindTextureDescriptorIndex(texOp); - - if (pCount == 3 && isArray) - { - // The array index is not scaled, just x and y. - vector = "ivec3(Helper_TexelFetchScale((" + vector + ").xy, " + index + "), (" + vector + ").z)"; - } - else if (pCount == 2 && !isArray) - { - vector = "Helper_TexelFetchScale(" + vector + ", " + index + ")"; - } - } - } - - return vector; - } - - string ApplyBias(string vector) - { - int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision(); - if (isGather && gatherBiasPrecision != 0) - { - // GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels. - // Offset by the gather precision divided by 2 to correct for rounding. - - if (pCount == 1) - { - vector = $"{vector} + (1.0 / (float(textureSize({samplerName}, 0)) * float({1 << (gatherBiasPrecision + 1)})))"; - } - else - { - vector = $"{vector} + (1.0 / (vec{pCount}(textureSize({samplerName}, 0).{"xyz".Substring(0, pCount)}) * float({1 << (gatherBiasPrecision + 1)})))"; - } - } - - return vector; - } - - Append(ApplyBias(ApplyScaling(AssemblePVector(pCount)))); + Append(AssemblePVector(pCount)); string AssembleDerivativesVector(int count) { @@ -750,7 +680,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else { - (TextureDescriptor descriptor, int descriptorIndex) = context.Config.FindTextureDescriptor(texOp); + TextureDescriptor descriptor = context.Config.FindTextureDescriptor(texOp); bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer; string texCall; @@ -767,14 +697,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCall = $"textureSize({samplerName}){GetMask(texOp.Index)}"; } - if (context.Config.Stage.SupportsRenderScale() && - (texOp.Index < 2 || (texOp.Type & SamplerType.Mask) == SamplerType.Texture3D) && - !isBindless && - !isIndexed) - { - texCall = $"Helper_TextureSizeUnscale({texCall}, {descriptorIndex})"; - } - return texCall; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 0ef89b398..7af6d316e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Dictionary OutputsPerPatch { get; } = new Dictionary(); public Instruction CoordTemp { get; set; } + public StructuredFunction CurrentFunction { get; set; } private readonly Dictionary _locals = new Dictionary(); private readonly Dictionary _localForArgs = new Dictionary(); private readonly Dictionary _funcArgs = new Dictionary(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index fda0dc47c..eb64f8241 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Numerics; using static Spv.Specification; @@ -114,6 +113,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.MemoryBarrier, GenerateMemoryBarrier); Add(Instruction.Minimum, GenerateMinimum); Add(Instruction.MinimumU32, GenerateMinimumU32); + Add(Instruction.Modulo, GenerateModulo); Add(Instruction.Multiply, GenerateMultiply); Add(Instruction.MultiplyHighS32, GenerateMultiplyHighS32); Add(Instruction.MultiplyHighU32, GenerateMultiplyHighU32); @@ -744,8 +744,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv pCoords = Src(AggregateType.S32); } - pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords: true, isBindless, isIndexed, isArray, pCount); - (var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; var image = context.Load(imageType, imageVariable); @@ -1040,6 +1038,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GenerateBinaryU32(context, operation, context.Delegates.GlslUMin); } + private static OperationResult GenerateModulo(CodeGenContext context, AstOperation operation) + { + return GenerateBinary(context, operation, context.Delegates.FMod, null); + } + private static OperationResult GenerateMultiply(CodeGenContext context, AstOperation operation) { return GenerateBinary(context, operation, context.Delegates.FMul, context.Delegates.IMul); @@ -1101,7 +1104,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation) { - context.Return(); + if (operation.SourcesCount != 0) + { + context.ReturnValue(context.Get(context.CurrentFunction.ReturnType, operation.GetSource(0))); + } + else + { + context.Return(); + } + return OperationResult.Invalid; } @@ -1439,35 +1450,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - SpvInstruction ApplyBias(SpvInstruction vector, SpvInstruction image) - { - int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision(); - if (isGather && gatherBiasPrecision != 0) - { - // GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels. - // Offset by the gather precision divided by 2 to correct for rounding. - var sizeType = pCount == 1 ? context.TypeS32() : context.TypeVector(context.TypeS32(), pCount); - var pVectorType = pCount == 1 ? context.TypeFP32() : context.TypeVector(context.TypeFP32(), pCount); - - var bias = context.Constant(context.TypeFP32(), (float)(1 << (gatherBiasPrecision + 1))); - var biasVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(bias, pCount).ToArray()); - - var one = context.Constant(context.TypeFP32(), 1f); - var oneVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(one, pCount).ToArray()); - - var divisor = context.FMul( - pVectorType, - context.ConvertSToF(pVectorType, context.ImageQuerySize(sizeType, image)), - biasVector); - - vector = context.FAdd(pVectorType, vector, context.FDiv(pVectorType, oneVector, divisor)); - } - - return vector; - } - SpvInstruction pCoords = AssemblePVector(pCount); - pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount); SpvInstruction AssembleDerivativesVector(int count) { @@ -1638,8 +1621,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv image = context.Image(imageType, image); } - pCoords = ApplyBias(pCoords, image); - var operands = operandsList.ToArray(); SpvInstruction result; @@ -1755,11 +1736,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv result = context.CompositeExtract(context.TypeS32(), result, (SpvLiteralInteger)texOp.Index); } - if (texOp.Index < 2 || (type & SamplerType.Mask) == SamplerType.Texture3D) - { - result = ScalingHelpers.ApplyUnscaling(context, texOp.WithType(type), result, isBindless, isIndexed); - } - return new OperationResult(AggregateType.S32, result); } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs deleted file mode 100644 index c8b21e881..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs +++ /dev/null @@ -1,227 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using static Spv.Specification; - -namespace Ryujinx.Graphics.Shader.CodeGen.Spirv -{ - using SpvInstruction = Spv.Generator.Instruction; - - static class ScalingHelpers - { - public static SpvInstruction ApplyScaling( - CodeGenContext context, - AstTextureOperation texOp, - SpvInstruction vector, - bool intCoords, - bool isBindless, - bool isIndexed, - bool isArray, - int pCount) - { - if (intCoords) - { - if (context.Config.Stage.SupportsRenderScale() && - !isBindless && - !isIndexed) - { - int index = texOp.Inst == Instruction.ImageLoad - ? context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp) - : context.Config.FindTextureDescriptorIndex(texOp); - - if (pCount == 3 && isArray) - { - return ApplyScaling2DArray(context, vector, index); - } - else if (pCount == 2 && !isArray) - { - return ApplyScaling2D(context, vector, index); - } - } - } - - return vector; - } - - private static SpvInstruction ApplyScaling2DArray(CodeGenContext context, SpvInstruction vector, int index) - { - // The array index is not scaled, just x and y. - var vectorXY = context.VectorShuffle(context.TypeVector(context.TypeS32(), 2), vector, vector, 0, 1); - var vectorZ = context.CompositeExtract(context.TypeS32(), vector, 2); - var vectorXYScaled = ApplyScaling2D(context, vectorXY, index); - var vectorScaled = context.CompositeConstruct(context.TypeVector(context.TypeS32(), 3), vectorXYScaled, vectorZ); - - return vectorScaled; - } - - private static SpvInstruction ApplyScaling2D(CodeGenContext context, SpvInstruction vector, int index) - { - var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); - var fieldIndex = context.Constant(context.TypeU32(), 4); - var scaleIndex = context.Constant(context.TypeU32(), index); - - if (context.Config.Stage == ShaderStage.Vertex) - { - var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32()); - var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3)); - var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer); - - scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount); - } - - scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1)); - - var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex); - var scale = context.Load(context.TypeFP32(), scaleElemPointer); - - var ivector2Type = context.TypeVector(context.TypeS32(), 2); - var localVector = context.CoordTemp; - - var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f)); - - var mergeLabel = context.Label(); - - if (context.Config.Stage == ShaderStage.Fragment) - { - var scaledInterpolatedLabel = context.Label(); - var scaledNoInterpolationLabel = context.Label(); - - var needsInterpolation = context.FOrdLessThan(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 0f)); - - context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone); - context.BranchConditional(needsInterpolation, scaledInterpolatedLabel, scaledNoInterpolationLabel); - - // scale < 0.0 - context.AddLabel(scaledInterpolatedLabel); - - ApplyScalingInterpolated(context, localVector, vector, scale); - context.Branch(mergeLabel); - - // scale >= 0.0 - context.AddLabel(scaledNoInterpolationLabel); - - ApplyScalingNoInterpolation(context, localVector, vector, scale); - context.Branch(mergeLabel); - - context.AddLabel(mergeLabel); - - var passthroughLabel = context.Label(); - var finalMergeLabel = context.Label(); - - context.SelectionMerge(finalMergeLabel, SelectionControlMask.MaskNone); - context.BranchConditional(passthrough, passthroughLabel, finalMergeLabel); - - context.AddLabel(passthroughLabel); - - context.Store(localVector, vector); - context.Branch(finalMergeLabel); - - context.AddLabel(finalMergeLabel); - - return context.Load(ivector2Type, localVector); - } - else - { - var passthroughLabel = context.Label(); - var scaledLabel = context.Label(); - - context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone); - context.BranchConditional(passthrough, passthroughLabel, scaledLabel); - - // scale == 1.0 - context.AddLabel(passthroughLabel); - - context.Store(localVector, vector); - context.Branch(mergeLabel); - - // scale != 1.0 - context.AddLabel(scaledLabel); - - ApplyScalingNoInterpolation(context, localVector, vector, scale); - context.Branch(mergeLabel); - - context.AddLabel(mergeLabel); - - return context.Load(ivector2Type, localVector); - } - } - - private static void ApplyScalingInterpolated(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale) - { - var vector2Type = context.TypeVector(context.TypeFP32(), 2); - - var scaleNegated = context.FNegate(context.TypeFP32(), scale); - var scaleVector = context.CompositeConstruct(vector2Type, scaleNegated, scaleNegated); - - var vectorFloat = context.ConvertSToF(vector2Type, vector); - var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated); - - var fragCoordPointer = context.Inputs[new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)]; - var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer); - var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1); - - var scaleMod = context.FMod(vector2Type, fragCoordXY, scaleVector); - var vectorInterpolated = context.FAdd(vector2Type, vectorScaled, scaleMod); - - context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorInterpolated)); - } - - private static void ApplyScalingNoInterpolation(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale) - { - if (context.Config.Stage == ShaderStage.Vertex) - { - scale = context.GlslFAbs(context.TypeFP32(), scale); - } - - var vector2Type = context.TypeVector(context.TypeFP32(), 2); - - var vectorFloat = context.ConvertSToF(vector2Type, vector); - var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scale); - - context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorScaled)); - } - - public static SpvInstruction ApplyUnscaling( - CodeGenContext context, - AstTextureOperation texOp, - SpvInstruction size, - bool isBindless, - bool isIndexed) - { - if (context.Config.Stage.SupportsRenderScale() && - !isBindless && - !isIndexed) - { - int index = context.Config.FindTextureDescriptorIndex(texOp); - - var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); - var fieldIndex = context.Constant(context.TypeU32(), 4); - var scaleIndex = context.Constant(context.TypeU32(), index); - - if (context.Config.Stage == ShaderStage.Vertex) - { - var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32()); - var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3)); - var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer); - - scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount); - } - - scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1)); - - var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex); - var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer)); - - var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f)); - - var sizeFloat = context.ConvertSToF(context.TypeFP32(), size); - var sizeUnscaled = context.FDiv(context.TypeFP32(), sizeFloat, scale); - var sizeUnscaledInt = context.ConvertFToS(context.TypeS32(), sizeUnscaled); - - return context.Select(context.TypeS32(), passthrough, size, sizeUnscaledInt); - } - - return size; - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs index 3ccfd7f55..0fa954e15 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs @@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public readonly FuncBinaryInstruction GlslSMax; public readonly FuncBinaryInstruction GlslFMin; public readonly FuncBinaryInstruction GlslSMin; + public readonly FuncBinaryInstruction FMod; public readonly FuncBinaryInstruction FMul; public readonly FuncBinaryInstruction IMul; public readonly FuncBinaryInstruction FSub; @@ -174,6 +175,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv GlslSMax = context.GlslSMax; GlslFMin = context.GlslFMin; GlslSMin = context.GlslSMin; + FMod = context.FMod; FMul = context.FMul; IMul = context.IMul; FSub = context.FSub; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 3e11a9749..a55e09fd3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -144,10 +144,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex) { - var function = info.Functions[funcIndex]; - - (_, var spvFunc) = context.GetFunction(funcIndex); + (var function, var spvFunc) = context.GetFunction(funcIndex); + context.CurrentFunction = function; context.AddFunction(spvFunc); context.StartFunction(); diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 817755bbe..f7afe5071 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -97,6 +97,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation MemoryBarrier, Minimum, MinimumU32, + Modulo, Multiply, MultiplyHighS32, MultiplyHighU32, diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 3434e2a81..2efcbca4f 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -4,10 +4,6 @@ net7.0 - - - - @@ -25,9 +21,6 @@ - - - diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index a44f13cc0..6c27279c9 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -27,10 +27,5 @@ namespace Ryujinx.Graphics.Shader.StructuredIr CbufSlot = cbufSlot; Handle = handle; } - - public AstTextureOperation WithType(SamplerType type) - { - return new AstTextureOperation(Inst, type, Format, Flags, CbufSlot, Handle, Index); - } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index ab8132540..44f0fad95 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -104,6 +104,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.MaximumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); Add(Instruction.Minimum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.MinimumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); + Add(Instruction.Modulo, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.Multiply, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.MultiplyHighS32, AggregateType.S32, AggregateType.S32, AggregateType.S32); Add(Instruction.MultiplyHighU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 939a52f3e..6846245e6 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { static class StructuredProgram { - public static StructuredProgramInfo MakeStructuredProgram(Function[] functions, ShaderConfig config) + public static StructuredProgramInfo MakeStructuredProgram(IReadOnlyList functions, ShaderConfig config) { StructuredProgramContext context = new StructuredProgramContext(config); - for (int funcIndex = 0; funcIndex < functions.Length; funcIndex++) + for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { Function function = functions[funcIndex]; diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 0c51b16f4..2786caaa0 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -49,13 +49,17 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly List _operations; private readonly Dictionary _labels; - public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) + public EmitterContext() + { + _operations = new List(); + _labels = new Dictionary(); + } + + public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) : this() { Program = program; Config = config; IsNonMain = isNonMain; - _operations = new List(); - _labels = new Dictionary(); EmitStart(); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index e41a28f12..6d4104cee 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -307,6 +307,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(fpType | Instruction.Minimum, Local(), a, b); } + public static Operand FPModulo(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) + { + return context.Add(fpType | Instruction.Modulo, Local(), a, b); + } + public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) { return context.Add(fpType | Instruction.Multiply, Local(), a, b); @@ -656,7 +661,6 @@ namespace Ryujinx.Graphics.Shader.Translation public static void Return(this EmitterContext context, Operand returnValue) { - context.PrepareForReturn(); context.Add(Instruction.Return, null, returnValue); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs new file mode 100644 index 000000000..206facd46 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs @@ -0,0 +1,134 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + class HelperFunctionManager + { + private readonly List _functionList; + private readonly Dictionary _functionIds; + private readonly ShaderStage _stage; + + public HelperFunctionManager(List functionList, ShaderStage stage) + { + _functionList = functionList; + _functionIds = new Dictionary(); + _stage = stage; + } + + public int GetOrCreateFunctionId(HelperFunctionName functionName) + { + if (_functionIds.TryGetValue(functionName, out int functionId)) + { + return functionId; + } + + Function function = GenerateFunction(functionName); + functionId = _functionList.Count; + _functionList.Add(function); + _functionIds.Add(functionName, functionId); + + return functionId; + } + + private Function GenerateFunction(HelperFunctionName functionName) + { + return functionName switch + { + HelperFunctionName.TexelFetchScale => GenerateTexelFetchScaleFunction(), + HelperFunctionName.TextureSizeUnscale => GenerateTextureSizeUnscaleFunction(), + _ => throw new ArgumentException($"Invalid function name {functionName}") + }; + } + + private Function GenerateTexelFetchScaleFunction() + { + EmitterContext context = new EmitterContext(); + + Operand input = Argument(0); + Operand samplerIndex = Argument(1); + Operand index = GetScaleIndex(context, samplerIndex); + + Operand scale = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index); + + Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f)); + Operand lblScaleNotOne = Label(); + + context.BranchIfFalse(lblScaleNotOne, scaleIsOne); + context.Return(input); + context.MarkLabel(lblScaleNotOne); + + int inArgumentsCount; + + if (_stage == ShaderStage.Fragment) + { + Operand scaleIsLessThanZero = context.FPCompareLess(scale, ConstF(0f)); + Operand lblScaleGreaterOrEqualZero = Label(); + + context.BranchIfFalse(lblScaleGreaterOrEqualZero, scaleIsLessThanZero); + + Operand negScale = context.FPNegate(scale); + Operand inputScaled = context.FPMultiply(context.IConvertS32ToFP32(input), negScale); + Operand fragCoordX = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0)); + Operand fragCoordY = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1)); + Operand fragCoord = context.ConditionalSelect(Argument(2), fragCoordY, fragCoordX); + Operand inputBias = context.FPModulo(fragCoord, negScale); + Operand inputWithBias = context.FPAdd(inputScaled, inputBias); + + context.Return(context.FP32ConvertToS32(inputWithBias)); + context.MarkLabel(lblScaleGreaterOrEqualZero); + + inArgumentsCount = 3; + } + else + { + inArgumentsCount = 2; + } + + Operand inputScaled2 = context.FPMultiply(context.IConvertS32ToFP32(input), scale); + + context.Return(context.FP32ConvertToS32(inputScaled2)); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TexelFetchScale", true, inArgumentsCount, 0); + } + + private Function GenerateTextureSizeUnscaleFunction() + { + EmitterContext context = new EmitterContext(); + + Operand input = Argument(0); + Operand samplerIndex = Argument(1); + Operand index = GetScaleIndex(context, samplerIndex); + + Operand scale = context.FPAbsolute(context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index)); + + Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f)); + Operand lblScaleNotOne = Label(); + + context.BranchIfFalse(lblScaleNotOne, scaleIsOne); + context.Return(input); + context.MarkLabel(lblScaleNotOne); + + Operand inputUnscaled = context.FPDivide(context.IConvertS32ToFP32(input), scale); + + context.Return(context.FP32ConvertToS32(inputUnscaled)); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TextureSizeUnscale", true, 2, 0); + } + + private Operand GetScaleIndex(EmitterContext context, Operand index) + { + switch (_stage) + { + case ShaderStage.Vertex: + Operand fragScaleCount = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.FragmentRenderScaleCount)); + return context.IAdd(Const(1), context.IAdd(index, fragScaleCount)); + default: + return context.IAdd(Const(1), index); + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs new file mode 100644 index 000000000..5accdf65f --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs @@ -0,0 +1,11 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation +{ + enum HelperFunctionName + { + TexelFetchScale, + TextureSizeUnscale + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 711661c9b..6b55a484b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation { static class Rewriter { - public static void RunPass(BasicBlock[] blocks, ShaderConfig config) + public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) { bool isVertexShader = config.Stage == ShaderStage.Vertex; bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); @@ -54,9 +54,14 @@ namespace Ryujinx.Graphics.Shader.Translation if (operation is TextureOperation texOp) { + node = InsertTexelFetchScale(hfm, node, config); + node = InsertTextureSizeUnscale(hfm, node, config); + if (texOp.Inst == Instruction.TextureSample) { - node = RewriteTextureSample(node, config); + node = InsertCoordNormalization(node, config); + node = InsertCoordGatherBias(node, config); + node = InsertConstOffsets(node, config); if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat) { @@ -344,10 +349,279 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static LinkedListNode RewriteTextureSample(LinkedListNode node, ShaderConfig config) + private static LinkedListNode InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode node, ShaderConfig config) { TextureOperation texOp = (TextureOperation)node.Value; + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + bool isImage = IsImageInstructionWithScale(texOp.Inst); + + if ((texOp.Inst == Instruction.TextureSample || isImage) && + intCoords && + !isBindless && + !isIndexed && + config.Stage.SupportsRenderScale() && + TypeSupportsScale(texOp.Type)) + { + int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale); + int samplerIndex = isImage + ? config.GetTextureDescriptors().Length + config.FindImageDescriptorIndex(texOp) + : config.FindTextureDescriptorIndex(texOp); + + for (int index = 0; index < coordsCount; index++) + { + Operand scaledCoord = Local(); + Operand[] callArgs; + + if (config.Stage == ShaderStage.Fragment) + { + callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex), Const(index) }; + } + else + { + callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex) }; + } + + node.List.AddBefore(node, new Operation(Instruction.Call, 0, scaledCoord, callArgs)); + + texOp.SetSource(coordsIndex + index, scaledCoord); + } + } + + return node; + } + + private static LinkedListNode InsertTextureSizeUnscale(HelperFunctionManager hfm, LinkedListNode node, ShaderConfig config) + { + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + if (texOp.Inst == Instruction.TextureSize && + texOp.Index < 2 && + !isBindless && + !isIndexed && + config.Stage.SupportsRenderScale() && + TypeSupportsScale(texOp.Type)) + { + int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale); + int samplerIndex = config.FindTextureDescriptorIndex(texOp, ignoreType: true); + + for (int index = texOp.DestsCount - 1; index >= 0; index--) + { + Operand dest = texOp.GetDest(index); + + Operand unscaledSize = Local(); + + // Replace all uses with the unscaled size value. + // This must be done before the call is added, since it also is a use of the original size. + foreach (INode useOp in dest.UseOps) + { + for (int srcIndex = 0; srcIndex < useOp.SourcesCount; srcIndex++) + { + if (useOp.GetSource(srcIndex) == dest) + { + useOp.SetSource(srcIndex, unscaledSize); + } + } + } + + Operand[] callArgs = new Operand[] { Const(functionId), dest, Const(samplerIndex) }; + + node.List.AddAfter(node, new Operation(Instruction.Call, 0, unscaledSize, callArgs)); + } + } + + return node; + } + + private static bool IsImageInstructionWithScale(Instruction inst) + { + // Currently, we don't support scaling images that are modified, + // so we only need to care about the load instruction. + return inst == Instruction.ImageLoad; + } + + private static bool TypeSupportsScale(SamplerType type) + { + return (type & SamplerType.Mask) == SamplerType.Texture2D; + } + + private static LinkedListNode InsertCoordNormalization(LinkedListNode node, ShaderConfig config) + { + // Emulate non-normalized coordinates by normalizing the coordinates on the shader. + // Without normalization, the coordinates are expected to the in the [0, W or H] range, + // and otherwise, it is expected to be in the [0, 1] range. + // We normalize by dividing the coords by the texture size. + + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); + + if (isCoordNormalized || intCoords) + { + return node; + } + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; + + for (int index = 0; index < normCoordsCount; index++) + { + Operand coordSize = Local(); + + Operand[] texSizeSources; + + if (isBindless || isIndexed) + { + texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) }; + } + else + { + texSizeSources = new Operand[] { Const(0) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureSize, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.CbufSlot, + texOp.Handle, + index, + new[] { coordSize }, + texSizeSources)); + + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + + Operand source = texOp.GetSource(coordsIndex + index); + + Operand coordNormalized = Local(); + + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize))); + + texOp.SetSource(coordsIndex + index, coordNormalized); + } + + return node; + } + + private static LinkedListNode InsertCoordGatherBias(LinkedListNode node, ShaderConfig config) + { + // The gather behavior when the coordinate sits right in the middle of two texels is not well defined. + // To ensure the correct texel is sampled, we add a small bias value to the coordinate. + // This value is calculated as the minimum value required to change the texel it will sample from, + // and is 0 if the host does not require the bias. + + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + + int gatherBiasPrecision = config.GpuAccessor.QueryHostGatherBiasPrecision(); + + if (!isGather || gatherBiasPrecision == 0) + { + return node; + } + + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; + + for (int index = 0; index < normCoordsCount; index++) + { + Operand coordSize = Local(); + Operand scaledSize = Local(); + Operand bias = Local(); + + Operand[] texSizeSources; + + if (isBindless || isIndexed) + { + texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) }; + } + else + { + texSizeSources = new Operand[] { Const(0) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureSize, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.CbufSlot, + texOp.Handle, + index, + new[] { coordSize }, + texSizeSources)); + + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Multiply, + scaledSize, + GenerateI2f(node, coordSize), + ConstF((float)(1 << (gatherBiasPrecision + 1))))); + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, bias, ConstF(1f), scaledSize)); + + Operand source = texOp.GetSource(coordsIndex + index); + + Operand coordBiased = Local(); + + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordBiased, source, bias)); + + texOp.SetSource(coordsIndex + index, coordBiased); + } + + return node; + } + + private static LinkedListNode InsertConstOffsets(LinkedListNode node, ShaderConfig config) + { + // Non-constant texture offsets are not allowed (according to the spec), + // however some GPUs does support that. + // For GPUs where it is not supported, we can replace the instruction with the following: + // For texture*Offset, we replace it by texture*, and add the offset to the P coords. + // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). + // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. + // For textureGatherOffset, we split the operation into up to 4 operations, one for each component + // that is accessed, where each textureGather operation has a different offset for each pixel. + + TextureOperation texOp = (TextureOperation)node.Value; + bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; @@ -355,9 +629,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); - - if (!hasInvalidOffset && isCoordNormalized) + if (!hasInvalidOffset) { return node; } @@ -454,7 +726,7 @@ namespace Ryujinx.Graphics.Shader.Translation hasInvalidOffset &= !areAllOffsetsConstant; - if (!hasInvalidOffset && isCoordNormalized) + if (!hasInvalidOffset) { return node; } @@ -473,63 +745,6 @@ namespace Ryujinx.Graphics.Shader.Translation int componentIndex = texOp.Index; - Operand Float(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value)); - - return res; - } - - // Emulate non-normalized coordinates by normalizing the coordinates on the shader. - // Without normalization, the coordinates are expected to the in the [0, W or H] range, - // and otherwise, it is expected to be in the [0, 1] range. - // We normalize by dividing the coords by the texture size. - if (!isCoordNormalized && !intCoords) - { - config.SetUsedFeature(FeatureFlags.IntegerSampling); - - int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; - - for (int index = 0; index < normCoordsCount; index++) - { - Operand coordSize = Local(); - - Operand[] texSizeSources; - - if (isBindless || isIndexed) - { - texSizeSources = new Operand[] { sources[0], Const(0) }; - } - else - { - texSizeSources = new Operand[] { Const(0) }; - } - - node.List.AddBefore(node, new TextureOperation( - Instruction.TextureSize, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.CbufSlot, - texOp.Handle, - index, - new[] { coordSize }, - texSizeSources)); - - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - - Operand source = sources[coordsIndex + index]; - - Operand coordNormalized = Local(); - - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, Float(coordSize))); - - sources[coordsIndex + index] = coordNormalized; - } - } - Operand[] dests = new Operand[texOp.DestsCount]; for (int i = 0; i < texOp.DestsCount; i++) @@ -541,15 +756,7 @@ namespace Ryujinx.Graphics.Shader.Translation LinkedListNode oldNode = node; - // Technically, non-constant texture offsets are not allowed (according to the spec), - // however some GPUs does support that. - // For GPUs where it is not supported, we can replace the instruction with the following: - // For texture*Offset, we replace it by texture*, and add the offset to the P coords. - // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). - // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. - // For textureGatherOffset, we split the operation into up to 4 operations, one for each component - // that is accessed, where each textureGather operation has a different offset for each pixel. - if (hasInvalidOffset && isGather && !isShadow) + if (isGather && !isShadow) { config.SetUsedFeature(FeatureFlags.IntegerSampling); @@ -557,7 +764,7 @@ namespace Ryujinx.Graphics.Shader.Translation sources.CopyTo(newSources, 0); - Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount); + Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); int destIndex = 0; @@ -576,7 +783,11 @@ namespace Ryujinx.Graphics.Shader.Translation Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)]; - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index]))); + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Divide, + offset, + GenerateI2f(node, intOffset), + GenerateI2f(node, texSizes[index]))); Operand source = sources[coordsIndex + index]; @@ -603,45 +814,46 @@ namespace Ryujinx.Graphics.Shader.Translation } else { - if (hasInvalidOffset) + if (intCoords) { - if (intCoords) + for (int index = 0; index < coordsCount; index++) { - for (int index = 0; index < coordsCount; index++) - { - Operand source = sources[coordsIndex + index]; + Operand source = sources[coordsIndex + index]; - Operand coordPlusOffset = Local(); + Operand coordPlusOffset = Local(); - node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); + node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); - sources[coordsIndex + index] = coordPlusOffset; - } + sources[coordsIndex + index] = coordPlusOffset; } - else + } + else + { + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); + + for (int index = 0; index < coordsCount; index++) { - config.SetUsedFeature(FeatureFlags.IntegerSampling); + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount); + Operand offset = Local(); - for (int index = 0; index < coordsCount; index++) - { - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + Operand intOffset = offsets[index]; - Operand offset = Local(); + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Divide, + offset, + GenerateI2f(node, intOffset), + GenerateI2f(node, texSizes[index]))); - Operand intOffset = offsets[index]; + Operand source = sources[coordsIndex + index]; - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index]))); + Operand coordPlusOffset = Local(); - Operand source = sources[coordsIndex + index]; + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset)); - Operand coordPlusOffset = Local(); - - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset)); - - sources[coordsIndex + index] = coordPlusOffset; - } + sources[coordsIndex + index] = coordPlusOffset; } } @@ -669,22 +881,13 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static Operand[] InsertTextureSize( + private static Operand[] InsertTextureLod( LinkedListNode node, TextureOperation texOp, Operand[] lodSources, Operand bindlessHandle, int coordsCount) { - Operand Int(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); - - return res; - } - Operand[] texSizes = new Operand[coordsCount]; Operand lod = Local(); @@ -708,11 +911,11 @@ namespace Ryujinx.Graphics.Shader.Translation if (bindlessHandle != null) { - texSizeSources = new Operand[] { bindlessHandle, Int(lod) }; + texSizeSources = new Operand[] { bindlessHandle, GenerateF2i(node, lod) }; } else { - texSizeSources = new Operand[] { Int(lod) }; + texSizeSources = new Operand[] { GenerateF2i(node, lod) }; } node.List.AddBefore(node, new TextureOperation( @@ -796,6 +999,24 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } + private static Operand GenerateI2f(LinkedListNode node, Operand value) + { + Operand res = Local(); + + node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value)); + + return res; + } + + private static Operand GenerateF2i(LinkedListNode node, Operand value) + { + Operand res = Local(); + + node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); + + return res; + } + private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode node, Operation operation) { Operand GenerateLoad(IoVariable ioVariable) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 775607972..73525cb27 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -860,7 +860,7 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors; } - public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp) + public TextureDescriptor FindTextureDescriptor(AstTextureOperation texOp) { TextureDescriptor[] descriptors = GetTextureDescriptors(); @@ -872,11 +872,11 @@ namespace Ryujinx.Graphics.Shader.Translation descriptor.HandleIndex == texOp.Handle && descriptor.Format == texOp.Format) { - return (descriptor, i); + return descriptor; } } - return (default, -1); + return default; } private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp) @@ -897,12 +897,30 @@ namespace Ryujinx.Graphics.Shader.Translation return -1; } - public int FindTextureDescriptorIndex(AstTextureOperation texOp) + private static int FindDescriptorIndex(TextureDescriptor[] array, TextureOperation texOp, bool ignoreType = false) { - return FindDescriptorIndex(GetTextureDescriptors(), texOp); + for (int i = 0; i < array.Length; i++) + { + var descriptor = array[i]; + + if ((descriptor.Type == texOp.Type || ignoreType) && + descriptor.CbufSlot == texOp.CbufSlot && + descriptor.HandleIndex == texOp.Handle && + descriptor.Format == texOp.Format) + { + return i; + } + } + + return -1; } - public int FindImageDescriptorIndex(AstTextureOperation texOp) + public int FindTextureDescriptorIndex(TextureOperation texOp, bool ignoreType = false) + { + return FindDescriptorIndex(GetTextureDescriptors(), texOp, ignoreType); + } + + public int FindImageDescriptorIndex(TextureOperation texOp) { return FindDescriptorIndex(GetImageDescriptors(), texOp); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs index 53f1e8475..867e24379 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -1,11 +1,11 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; +using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.Translation { static class ShaderIdentifier { - public static ShaderIdentification Identify(Function[] functions, ShaderConfig config) + public static ShaderIdentification Identify(IReadOnlyList functions, ShaderConfig config) { if (config.Stage == ShaderStage.Geometry && config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles && @@ -20,12 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation return ShaderIdentification.None; } - private static bool IsLayerPassthroughGeometryShader(Function[] functions, out int layerInputAttr) + private static bool IsLayerPassthroughGeometryShader(IReadOnlyList functions, out int layerInputAttr) { bool writesLayer = false; layerInputAttr = 0; - if (functions.Length != 1) + if (functions.Count != 1) { return false; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index 87d97e52e..5bbc00097 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation.Optimizations; using System; +using System.Collections.Generic; using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -44,7 +45,14 @@ namespace Ryujinx.Graphics.Shader.Translation } } - Function[] funcs = new Function[functions.Length]; + List funcs = new List(functions.Length); + + for (int i = 0; i < functions.Length; i++) + { + funcs.Add(null); + } + + HelperFunctionManager hfm = new HelperFunctionManager(funcs, config.Stage); for (int i = 0; i < functions.Length; i++) { @@ -71,7 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation Ssa.Rename(cfg.Blocks); Optimizer.RunPass(cfg.Blocks, config); - Rewriter.RunPass(cfg.Blocks, config); + Rewriter.RunPass(hfm, cfg.Blocks, config); } funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); From 5b42a4d2c4703c826dc9c1b1dc54e5336dc6883d Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 25 May 2023 23:41:03 +0200 Subject: [PATCH 08/25] Fix mod names (#5088) --- src/Ryujinx.HLE/HOS/ModLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index bde4d11ce..ac983cfe5 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -167,12 +167,12 @@ namespace Ryujinx.HLE.HOS if (StrEquals(RomfsDir, modDir.Name)) { - mods.RomfsDirs.Add(mod = new Mod($"<{titleId} RomFs>", modDir)); + mods.RomfsDirs.Add(mod = new Mod(dir.Name, modDir)); types.Append('R'); } else if (StrEquals(ExefsDir, modDir.Name)) { - mods.ExefsDirs.Add(mod = new Mod($"<{titleId} ExeFs>", modDir)); + mods.ExefsDirs.Add(mod = new Mod(dir.Name, modDir)); types.Append('E'); } else if (StrEquals(CheatDir, modDir.Name)) From e6658c133ca8bef4a6612d6feabfc6180f0c8318 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 25 May 2023 23:42:49 -0300 Subject: [PATCH 09/25] Fix resolution scaling of image operation coordinates (#5102) * Fix resolution scaling of image operation coordinates * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index b8d74a463..f4d7f3b58 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4714; + private const uint CodeGenVersion = 5102; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 6b55a484b..eab0f9d2f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -366,7 +366,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool isImage = IsImageInstructionWithScale(texOp.Inst); if ((texOp.Inst == Instruction.TextureSample || isImage) && - intCoords && + (intCoords || isImage) && !isBindless && !isIndexed && config.Stage.SupportsRenderScale() && From 3b375525fbaded422fb2f9a9984a5770a3779fcb Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 26 May 2023 15:19:37 -0300 Subject: [PATCH 10/25] Force reciprocal operation with value biased by constant to be precise on macOS (#5110) * Force operations to be precise in some cases on SPIR-V * Make it a bit more strict, add comments * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Spirv/Instructions.cs | 8 +++--- .../IntermediateRepresentation/Operation.cs | 2 ++ .../StructuredIr/AstOperation.cs | 14 +++++++--- .../StructuredIr/AstTextureOperation.cs | 2 +- .../StructuredIr/StructuredProgram.cs | 26 ++++++++++++++++--- .../StructuredIr/StructuredProgramContext.cs | 2 +- .../Translation/Rewriter.cs | 25 ++++++++++++++++++ 8 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index f4d7f3b58..e2346bd0b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5102; + private const uint CodeGenVersion = 5110; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index eb64f8241..f088a47f3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -2245,7 +2245,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2256,7 +2256,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2316,7 +2316,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2327,7 +2327,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index 99179f151..d502a9b65 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public Instruction Inst { get; private set; } public StorageKind StorageKind { get; } + public bool ForcePrecise { get; set; } + private Operand[] _dests; public Operand Dest diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs index 2393fd8d8..4cf729d09 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs @@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { public Instruction Inst { get; } public StorageKind StorageKind { get; } + public bool ForcePrecise { get; } public int Index { get; } @@ -17,10 +18,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public int SourcesCount => _sources.Length; - public AstOperation(Instruction inst, StorageKind storageKind, IAstNode[] sources, int sourcesCount) + public AstOperation(Instruction inst, StorageKind storageKind, bool forcePrecise, IAstNode[] sources, int sourcesCount) { Inst = inst; StorageKind = storageKind; + ForcePrecise = forcePrecise; _sources = sources; for (int index = 0; index < sources.Length; index++) @@ -38,12 +40,18 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Index = 0; } - public AstOperation(Instruction inst, StorageKind storageKind, int index, IAstNode[] sources, int sourcesCount) : this(inst, storageKind, sources, sourcesCount) + public AstOperation( + Instruction inst, + StorageKind storageKind, + bool forcePrecise, + int index, + IAstNode[] sources, + int sourcesCount) : this(inst, storageKind, forcePrecise, sources, sourcesCount) { Index = index; } - public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, sources, sources.Length) + public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, false, sources, sources.Length) { } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index 6c27279c9..a4e097eb6 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr int cbufSlot, int handle, int index, - params IAstNode[] sources) : base(inst, StorageKind.None, index, sources, sources.Length) + params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) { Type = type; Format = format; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 6846245e6..4405c07aa 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -156,7 +156,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else { - source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount); + source = new AstOperation( + inst, + operation.StorageKind, + operation.ForcePrecise, + operation.Index, + sources, + operation.SourcesCount); } AggregateType destElemType = destType; @@ -179,7 +185,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr dest.VarType = destElemType; - context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, new[] { destVec, index }, 2))); + context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, false, new[] { destVec, index }, 2))); } } else if (operation.Dest != null) @@ -227,7 +233,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else if (!isCopy) { - source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount); + source = new AstOperation( + inst, + operation.StorageKind, + operation.ForcePrecise, + operation.Index, + sources, + operation.SourcesCount); } else { @@ -248,7 +260,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else { - context.AddNode(new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount)); + context.AddNode(new AstOperation( + inst, + operation.StorageKind, + operation.ForcePrecise, + operation.Index, + sources, + operation.SourcesCount)); } // Those instructions needs to be emulated by using helper functions, diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index c5ad3683a..a4d079914 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -319,7 +319,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr new AstOperand(OperandType.Constant, elemIndex) }; - return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, sources, sources.Length); + return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, false, sources, sources.Length); } return GetOperand(operand); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index eab0f9d2f..866ae5223 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) { bool isVertexShader = config.Stage == ShaderStage.Vertex; + bool isImpreciseFragmentShader = config.Stage == ShaderStage.Fragment && config.GpuAccessor.QueryHostReducedPrecision(); bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug(); bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat(); @@ -45,6 +46,11 @@ namespace Ryujinx.Graphics.Shader.Translation } } + if (isImpreciseFragmentShader) + { + EnableForcePreciseIfNeeded(operation); + } + if (hasVectorIndexingBug) { InsertVectorComponentSelect(node, config); @@ -81,6 +87,25 @@ namespace Ryujinx.Graphics.Shader.Translation } } + private static void EnableForcePreciseIfNeeded(Operation operation) + { + // There are some cases where a small bias is added to values to prevent division by zero. + // When operating with reduced precision, it is possible for this bias to get rounded to 0 + // and cause a division by zero. + // To prevent that, we force those operations to be precise even if the host wants + // imprecise operations for performance. + + if (operation.Inst == (Instruction.FP32 | Instruction.Divide) && + operation.GetSource(0).Type == OperandType.Constant && + operation.GetSource(0).AsFloat() == 1f && + operation.GetSource(1).AsgOp is Operation addOp && + addOp.Inst == (Instruction.FP32 | Instruction.Add) && + addOp.GetSource(1).Type == OperandType.Constant) + { + addOp.ForcePrecise = true; + } + } + private static void InsertVectorComponentSelect(LinkedListNode node, ShaderConfig config) { Operation operation = (Operation)node.Value; From 42b9c1e8fede88880454154f8c3683f1f8424ed9 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Fri, 26 May 2023 17:57:43 -0400 Subject: [PATCH 11/25] Ryujinx.Ava: fixes for random hangs on exit (#4827) * Attempt at fixing hang on exit by ending the WindowNotificationManager notification loop, so that the Thread running it can exit. * explicitly apply the NotificationManager template to allow the notification loop to begin * NotificationHelper - remove explicity call to ApplyTemplate(). Change to ManualResetEventSlim so we can cancel the Wait on it. * add a timeout to AudioRenderSystem.Stop()'s waiting for the termination signal, log a warning if this timeout occurs, and continue execution * NotifiationHelper - cancel first, the CompleteAdding() * Remove AudioRenderSystem._terminationEvent, redundant * NotificationHelper - use host.Closing event to trigger cancellation instead of _notifationManager.DetachedFromLogicalTree * Change NotificationHelper to use an explicit Thread for background work. Wait on the cancellationToken's WaitHandle so the Thread doesn't have to deal with async. Wrap foreach in try/catch (OperationCanceledException) to swallow the escaping exception from the GetConsumingEnumerable(). * adjust formatting of AsyncWorkQueue constructor to use object initializers consistently * use AsyncWorkQueue to do everything I added in SetNotificationManager() * Revert "use AsyncWorkQueue to do everything I added in SetNotificationManager()" This reverts commit f0e78366b8776ec8e2fef8ab023c0db1833155d3. * use AsyncWorkQueue to handle the Thread-related changes previously made to NotificationHelper.SetNotificationHelper(). Wrap it in Lazy and force instantiation in the TemplateApplied event handler to accomodate for the fact that AsyncWorkQueue starts immediately, and the notification dispatch loop was being delayed by _templateAppliedEvent. * impl changes suggested by AcK77 * impl changes suggested by AcK77 (more) --- .../Renderer/Server/AudioRenderSystem.cs | 14 -------- .../UI/Helpers/NotificationHelper.cs | 35 +++++++++++-------- src/Ryujinx.Common/AsyncWorkQueue.cs | 8 +++-- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 53570243d..1ad7b8590 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -31,7 +31,6 @@ namespace Ryujinx.Audio.Renderer.Server private AudioRendererRenderingDevice _renderingDevice; private AudioRendererExecutionMode _executionMode; private IWritableEvent _systemEvent; - private ManualResetEvent _terminationEvent; private MemoryPoolState _dspMemoryPoolState; private VoiceContext _voiceContext; private MixContext _mixContext; @@ -83,7 +82,6 @@ namespace Ryujinx.Audio.Renderer.Server public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent) { _manager = manager; - _terminationEvent = new ManualResetEvent(false); _dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); _voiceContext = new VoiceContext(); _mixContext = new MixContext(); @@ -387,11 +385,6 @@ namespace Ryujinx.Audio.Renderer.Server _isActive = false; } - if (_executionMode == AudioRendererExecutionMode.Auto) - { - _terminationEvent.WaitOne(); - } - Logger.Info?.Print(LogClass.AudioRenderer, $"Stopped renderer id {_sessionId}"); } @@ -668,8 +661,6 @@ namespace Ryujinx.Audio.Renderer.Server { if (_isActive) { - _terminationEvent.Reset(); - if (!_manager.Processor.HasRemainingCommands(_sessionId)) { GenerateCommandList(out CommandList commands); @@ -686,10 +677,6 @@ namespace Ryujinx.Audio.Renderer.Server _isDspRunningBehind = true; } } - else - { - _terminationEvent.Set(); - } } } @@ -857,7 +844,6 @@ namespace Ryujinx.Audio.Renderer.Server } _manager.Unregister(this); - _terminationEvent.Dispose(); _workBufferMemoryPin.Dispose(); if (MemoryManager is IRefCounted rc) diff --git a/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs index 7e2afb8bd..f207c5fb0 100644 --- a/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs @@ -3,21 +3,20 @@ using Avalonia.Controls; using Avalonia.Controls.Notifications; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Common; using System; using System.Collections.Concurrent; using System.Threading; -using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Helpers { public static class NotificationHelper { - private const int MaxNotifications = 4; + private const int MaxNotifications = 4; private const int NotificationDelayInMs = 5000; private static WindowNotificationManager _notificationManager; - private static readonly ManualResetEvent _templateAppliedEvent = new(false); private static readonly BlockingCollection _notifications = new(); public static void SetNotificationManager(Window host) @@ -29,25 +28,31 @@ namespace Ryujinx.Ava.UI.Helpers Margin = new Thickness(0, 0, 15, 40) }; + var maybeAsyncWorkQueue = new Lazy>( + () => new AsyncWorkQueue(notification => + { + Dispatcher.UIThread.Post(() => + { + _notificationManager.Show(notification); + }); + }, + "UI.NotificationThread", + _notifications), + LazyThreadSafetyMode.ExecutionAndPublication); + _notificationManager.TemplateApplied += (sender, args) => { - _templateAppliedEvent.Set(); + // NOTE: Force creation of the AsyncWorkQueue. + _ = maybeAsyncWorkQueue.Value; }; - Task.Run(async () => + host.Closing += (sender, args) => { - _templateAppliedEvent.WaitOne(); - - foreach (var notification in _notifications.GetConsumingEnumerable()) + if (maybeAsyncWorkQueue.IsValueCreated) { - Dispatcher.UIThread.Post(() => - { - _notificationManager.Show(notification); - }); - - await Task.Delay(NotificationDelayInMs / MaxNotifications); + maybeAsyncWorkQueue.Value.Dispose(); } - }); + }; } public static void Show(string title, string text, NotificationType type, bool waitingExit = false, Action onClick = null, Action onClose = null) diff --git a/src/Ryujinx.Common/AsyncWorkQueue.cs b/src/Ryujinx.Common/AsyncWorkQueue.cs index 80f8dcfe4..746ef4cac 100644 --- a/src/Ryujinx.Common/AsyncWorkQueue.cs +++ b/src/Ryujinx.Common/AsyncWorkQueue.cs @@ -22,9 +22,11 @@ namespace Ryujinx.Common _cts = new CancellationTokenSource(); _queue = collection; _workerAction = callback; - _workerThread = new Thread(DoWork) { Name = name }; - - _workerThread.IsBackground = true; + _workerThread = new Thread(DoWork) + { + Name = name, + IsBackground = true + }; _workerThread.Start(); } From 986ac9ff8330b61a734f91187bf4b97c16a452c7 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 28 May 2023 08:02:30 +0200 Subject: [PATCH 12/25] Use variables to configure job timeouts (#5123) --- .github/workflows/build.yml | 4 ++-- .github/workflows/flatpak.yml | 2 +- .github/workflows/nightly_pr_comment.yml | 2 +- .github/workflows/release.yml | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c6dc724c8..37b68448c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: build: name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] @@ -110,7 +110,7 @@ jobs: build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} strategy: matrix: configuration: [ Debug, Release ] diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 04b917c34..96bc77e16 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -12,7 +12,7 @@ concurrency: flatpak-release jobs: release: - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} runs-on: ubuntu-latest env: diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index 9ddd458c6..c7af4acad 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -7,7 +7,7 @@ jobs: pr_comment: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} steps: - uses: actions/github-script@v6 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 491fe6d9e..4018facab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: release: name: Release ${{ matrix.OS_NAME }} runs-on: ${{ matrix.os }} - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} strategy: matrix: os: [ ubuntu-latest, windows-latest ] @@ -144,7 +144,7 @@ jobs: macos_release: name: Release MacOS universal runs-on: ubuntu-latest - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} steps: - uses: actions/checkout@v3 @@ -207,4 +207,4 @@ jobs: needs: release with: ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit + secrets: inherit \ No newline at end of file From f3873620a339ef5c6b0cc2602ffe033134a1a300 Mon Sep 17 00:00:00 2001 From: Mary Date: Sun, 28 May 2023 08:08:47 +0200 Subject: [PATCH 13/25] actions: Workaround YAML limitation for timeout-minutes Because Github Actions wants an int, we use fromJSON to hack around this. --- .github/workflows/build.yml | 4 ++-- .github/workflows/flatpak.yml | 2 +- .github/workflows/nightly_pr_comment.yml | 2 +- .github/workflows/release.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37b68448c..efd7d2314 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: build: name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] @@ -110,7 +110,7 @@ jobs: build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} strategy: matrix: configuration: [ Debug, Release ] diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 96bc77e16..d4380e05f 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -12,7 +12,7 @@ concurrency: flatpak-release jobs: release: - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} runs-on: ubuntu-latest env: diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index c7af4acad..deabae670 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -7,7 +7,7 @@ jobs: pr_comment: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} steps: - uses: actions/github-script@v6 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4018facab..98ba34822 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: release: name: Release ${{ matrix.OS_NAME }} runs-on: ${{ matrix.os }} - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} strategy: matrix: os: [ ubuntu-latest, windows-latest ] @@ -144,7 +144,7 @@ jobs: macos_release: name: Release MacOS universal runs-on: ubuntu-latest - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} steps: - uses: actions/checkout@v3 From e8f5e97fa475e196da4102574725cabd8c9994d5 Mon Sep 17 00:00:00 2001 From: Mary Date: Sun, 28 May 2023 11:34:57 +0200 Subject: [PATCH 14/25] actions: revert timeout-minutes changes for PR workflow Varibales aren't exposed to PRs... --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index efd7d2314..d379437b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: build: name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} - timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} + timeout-minutes: 45 strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] @@ -110,7 +110,7 @@ jobs: build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest - timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} + timeout-minutes: 45 strategy: matrix: configuration: [ Debug, Release ] From 378080eb8721f7ef652f7a546efd00d0b821c80d Mon Sep 17 00:00:00 2001 From: Daniel Shala Date: Sun, 28 May 2023 22:44:46 +0200 Subject: [PATCH 15/25] Added Custom Path case when saving screenshots (#5086) --- src/Ryujinx.Ava/AppHost.cs | 2 +- src/Ryujinx/Ui/RendererWidgetBase.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index f3e90ef17..349b64b41 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -270,7 +270,7 @@ namespace Ryujinx.Ava string directory = AppDataManager.Mode switch { - AppDataManager.LaunchMode.Portable => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), + AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") }; diff --git a/src/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs index 573b69b35..e2cba7775 100644 --- a/src/Ryujinx/Ui/RendererWidgetBase.cs +++ b/src/Ryujinx/Ui/RendererWidgetBase.cs @@ -381,7 +381,7 @@ namespace Ryujinx.Ui string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; string directory = AppDataManager.Mode switch { - AppDataManager.LaunchMode.Portable => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"), + AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"), _ => System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") }; From 7e0b4bd538cbd4d9a7aff7f1936b781bb036998a Mon Sep 17 00:00:00 2001 From: siegmund-heiss-ich <119589995+siegmund-heiss-ich@users.noreply.github.com> Date: Sun, 28 May 2023 22:54:30 +0200 Subject: [PATCH 16/25] Improve macOS updater (#5064) * Fix macOS Updater (once again) * Also fix my brain's issues * Move set -e that lsof doesn't trigger exit 1 * Resolve yesterdays brain malfunction 2 * Revert "Move set -e that lsof doesn't trigger exit 1" This reverts commit 589a630610fff26f6a549d82c73be61b74187327. * Also check if PID exists * Remove lsof and instead check for running processes * Remove empty lines * Increase max iterations * Address feedback * Remove obsolete check for child processes * Update comments * Update comments * I swear this is the last commit * lsof + ps check --- distribution/macos/updater.sh | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/distribution/macos/updater.sh b/distribution/macos/updater.sh index 0854d4347..4d7dcdf23 100755 --- a/distribution/macos/updater.sh +++ b/distribution/macos/updater.sh @@ -25,14 +25,27 @@ error_handler() { exit 1 } -# Wait for Ryujinx to exit -# NOTE: in case no fds are open, lsof could be returning with a process still living. -# We wait 1s and assume the process stopped after that -lsof -p $APP_PID +r 1 &>/dev/null -sleep 1 - trap 'error_handler ${LINENO}' ERR +# Wait for Ryujinx to exit. +# If the main process is still acitve, we wait for 1 second and check it again. +# After the fifth time checking, this script exits with status 1. + +attempt=0 +while true; do + if lsof -p $APP_PID +r 1 &>/dev/null || ps -p "$APP_PID" &>/dev/null; then + if [ "$attempt" -eq 4 ]; then + exit 1 + fi + sleep 1 + else + break + fi + (( attempt++ )) +done + +sleep 1 + # Now replace and reopen. rm -rf "$INSTALL_DIRECTORY" mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY" From dd514a115ccdf44662868dbd4bc7b02254c1fe9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Arrouye?= <5017270+tarrouye@users.noreply.github.com> Date: Sun, 28 May 2023 14:03:27 -0700 Subject: [PATCH 17/25] Update LastPlayed date on emulation end. (#5056) --- src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs | 2 ++ src/Ryujinx/Ui/MainWindow.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index f8dd41435..e7013968d 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1529,6 +1529,8 @@ namespace Ryujinx.Ava.UI.ViewModels double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds; appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); } + + appMetadata.LastPlayed = DateTime.UtcNow; }); } diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index 7cae62227..1918594ce 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -1024,6 +1024,8 @@ namespace Ryujinx.Ui double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds; appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); } + + appMetadata.LastPlayed = DateTime.UtcNow; }); } } From c9e297b74c99f8269f82863a31495a47ee1f7b0d Mon Sep 17 00:00:00 2001 From: yell0wsuit <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 29 May 2023 04:13:40 +0700 Subject: [PATCH 18/25] About window: Add changelog link under ver. number (#5095) --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 4 +++- src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml | 12 ++++++++++ .../Ui/Windows/AboutWindow.Designer.cs | 22 +++++++++++++++++++ src/Ryujinx/Ui/Windows/AboutWindow.cs | 5 +++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index a7b490b91..03cddcc84 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -637,5 +637,7 @@ "SettingsTabNetworkInterface": "Network Interface:", "NetworkInterfaceTooltip": "The network interface used for LAN features", "NetworkInterfaceDefault": "Default", - "PackagingShaders": "Packaging Shaders" + "PackagingShaders": "Packaging Shaders", + "AboutChangelogButton": "View Changelog on GitHub", + "AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser." } \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml index 7bd3e20d3..28a851d67 100644 --- a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml @@ -72,6 +72,18 @@ LineHeight="12" Text="{Binding Version}" TextAlignment="Center" /> + Date: Sun, 28 May 2023 23:25:55 +0200 Subject: [PATCH 19/25] chore: Update Avalonia to 0.10.21 (#5124) --- Directory.Packages.props | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0bbe81406..5054689ca 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,11 +3,11 @@ true - - - - - + + + + + From dc0dbc50abdaedfdcca05e5a5c1a5f26f70e3b79 Mon Sep 17 00:00:00 2001 From: cstamford Date: Sun, 28 May 2023 22:31:56 +0100 Subject: [PATCH 20/25] Add support for VK_EXT_depth_clip_control. (#5027) * Add support for VK_EXT_depth_clip_control. * Code review feedback Minor formatting Co-authored-by: gdkchan * Check .DepthClipControl to make sure the host actually supports the feature. * Review feedback: remove Vulkan platform switch, relying on QueryHostSupportsDepthClipControl to drive the behaviour - OpenGL returns true, and any future platforms that don't support the [-1, 1] depth mode can return false for the transformation. --------- Co-authored-by: gdkchan --- src/Ryujinx.Graphics.GAL/Capabilities.cs | 3 +++ .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 2 ++ src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 1 + src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 +++++++ .../Translation/EmitterContext.cs | 4 +-- .../HardwareCapabilities.cs | 3 +++ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 8 ++++-- src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 18 +++++++++++++ .../VulkanInitialization.cs | 27 +++++++++++++++++++ src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 15 +++++++++++ 11 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index a93d38466..48b37d35d 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -39,6 +39,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsViewportMask; public readonly bool SupportsViewportSwizzle; public readonly bool SupportsIndirectParameters; + public readonly bool SupportsDepthClipControl; public readonly uint MaximumUniformBuffersPerStage; public readonly uint MaximumStorageBuffersPerStage; @@ -85,6 +86,7 @@ namespace Ryujinx.Graphics.GAL bool supportsViewportMask, bool supportsViewportSwizzle, bool supportsIndirectParameters, + bool supportsDepthClipControl, uint maximumUniformBuffersPerStage, uint maximumStorageBuffersPerStage, uint maximumTexturesPerStage, @@ -127,6 +129,7 @@ namespace Ryujinx.Graphics.GAL SupportsViewportMask = supportsViewportMask; SupportsViewportSwizzle = supportsViewportSwizzle; SupportsIndirectParameters = supportsIndirectParameters; + SupportsDepthClipControl = supportsDepthClipControl; MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage; MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage; MaximumTexturesPerStage = maximumTexturesPerStage; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index e2346bd0b..7f83f5880 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5110; + private const uint CodeGenVersion = 5027; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index e4e1e0d61..d206aad0b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -165,6 +165,8 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask; + public bool QueryHostSupportsDepthClipControl() => _context.Capabilities.SupportsDepthClipControl; + /// /// Converts a packed Maxwell texture format to the shader translator texture format. /// diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 7d5fe8931..161191b85 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -163,6 +163,7 @@ namespace Ryujinx.Graphics.OpenGL supportsViewportMask: HwCapabilities.SupportsViewportArray2, supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, + supportsDepthClipControl: true, maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver? maximumStorageBuffersPerStage: 16, maximumTexturesPerStage: 32, diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 2207156cd..3be5088e4 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -367,6 +367,15 @@ namespace Ryujinx.Graphics.Shader return true; } + /// + /// Queries whether the host supports depth clip control. + /// + /// True if the GPU and driver supports depth clip control, false otherwise + bool QueryHostSupportsDepthClipControl() + { + return true; + } + /// /// Queries the point size from the GPU state, used when it is not explicitly set on the shader. /// diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 2786caaa0..6ca74a379 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Shader.Translation this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne)); } - if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne()) + if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl()) { Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)); Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3)); @@ -283,7 +283,7 @@ namespace Ryujinx.Graphics.Shader.Translation oldYLocal = null; } - if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne()) + if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl()) { oldZLocal = Local(); this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2))); diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 4b9ace3aa..f600d93f0 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -43,6 +43,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsGeometryShader; public readonly bool SupportsViewportArray2; public readonly bool SupportsHostImportedMemory; + public readonly bool SupportsDepthClipControl; public readonly uint MinSubgroupSize; public readonly uint MaxSubgroupSize; public readonly ShaderStageFlags RequiredSubgroupSizeStages; @@ -79,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsGeometryShader, bool supportsViewportArray2, bool supportsHostImportedMemory, + bool supportsDepthClipControl, uint minSubgroupSize, uint maxSubgroupSize, ShaderStageFlags requiredSubgroupSizeStages, @@ -114,6 +116,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsGeometryShader = supportsGeometryShader; SupportsViewportArray2 = supportsViewportArray2; SupportsHostImportedMemory = supportsHostImportedMemory; + SupportsDepthClipControl = supportsDepthClipControl; MinSubgroupSize = minSubgroupSize; MaxSubgroupSize = maxSubgroupSize; RequiredSubgroupSizeStages = requiredSubgroupSizeStages; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index ce6148e2f..b4eccfbb2 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -813,8 +813,12 @@ namespace Ryujinx.Graphics.Vulkan public void SetDepthMode(DepthMode mode) { - // Currently this is emulated on the shader, because Vulkan had no support for changing the depth mode. - // In the future, we may want to use the VK_EXT_depth_clip_control extension to change it here. + bool oldMode = _newState.DepthMode; + _newState.DepthMode = mode == DepthMode.MinusOneToOne; + if (_newState.DepthMode != oldMode) + { + SignalStateChange(); + } } public void SetDepthTest(DepthTestDescriptor depthTest) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index a3d8dd6fb..aff3f13f0 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -304,6 +304,12 @@ namespace Ryujinx.Graphics.Vulkan set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4); } + public bool DepthMode + { + get => ((Internal.Id9 >> 6) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); + } + public NativeArray Stages; public NativeArray StageRequiredSubgroupSizes; public PipelineLayout PipelineLayout; @@ -331,6 +337,7 @@ namespace Ryujinx.Graphics.Vulkan LineWidth = 1f; SamplesCount = 1; + DepthMode = true; } public unsafe Auto CreateComputePipeline( @@ -482,6 +489,17 @@ namespace Ryujinx.Graphics.Vulkan PScissors = pScissors }; + if (gd.Capabilities.SupportsDepthClipControl) + { + var viewportDepthClipControlState = new PipelineViewportDepthClipControlCreateInfoEXT() + { + SType = StructureType.PipelineViewportDepthClipControlCreateInfoExt, + NegativeOneToOne = DepthMode + }; + + viewportState.PNext = &viewportDepthClipControlState; + } + var multisampleState = new PipelineMultisampleStateCreateInfo { SType = StructureType.PipelineMultisampleStateCreateInfo, diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 499a9ef78..51a3b129a 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -41,6 +41,7 @@ namespace Ryujinx.Graphics.Vulkan "VK_EXT_subgroup_size_control", "VK_NV_geometry_shader_passthrough", "VK_NV_viewport_array2", + "VK_EXT_depth_clip_control", "VK_KHR_portability_subset" // As per spec, we should enable this if present. }; @@ -345,6 +346,17 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesRobustness2; } + PhysicalDeviceDepthClipControlFeaturesEXT supportedFeaturesDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + { + SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, + PNext = features2.PNext + }; + + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control")) + { + features2.PNext = &supportedFeaturesDepthClipControl; + } + api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2); var supportedFeatures = features2.Features; @@ -507,6 +519,21 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresCustomBorderColor; } + PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl; + + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control") && + supportedFeaturesDepthClipControl.DepthClipControl) + { + featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + { + SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, + PNext = pExtendedFeatures, + DepthClipControl = true + }; + + pExtendedFeatures = &featuresDepthClipControl; + } + var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 4f3f72345..3987be9b4 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -216,6 +216,11 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt }; + PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + { + SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt + }; + PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR() { SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr @@ -244,6 +249,14 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &featuresCustomBorderColor; } + bool supportsDepthClipControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control"); + + if (supportsDepthClipControl) + { + featuresDepthClipControl.PNext = features2.PNext; + features2.PNext = &featuresDepthClipControl; + } + bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset"); if (usePortability) @@ -310,6 +323,7 @@ namespace Ryujinx.Graphics.Vulkan _physicalDevice.PhysicalDeviceFeatures.GeometryShader, _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), + supportsDepthClipControl && featuresDepthClipControl.DepthClipControl, propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, @@ -585,6 +599,7 @@ namespace Ryujinx.Graphics.Vulkan supportsViewportMask: Capabilities.SupportsViewportArray2, supportsViewportSwizzle: false, supportsIndirectParameters: true, + supportsDepthClipControl: Capabilities.SupportsDepthClipControl, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage, From 7bc9d0cdad5ce1b79d0f6a90b3ee6322db6aff39 Mon Sep 17 00:00:00 2001 From: subanz Date: Sun, 28 May 2023 16:54:22 -0500 Subject: [PATCH 21/25] Linux: Use gamemode if it is available when using Ryujinx.sh. (#4938) * Linux: Detect if gamemode is installed and start it when launching Ryujinx. When using the Ryujinx.sh script to start the emulator check if gamemoderun exists and use it if it does. Gamemode mode on Linux changes some system settings to make performance during gaming more consistent mainly by changing the CPU governor to performance. https://github.com/FeralInteractive/gamemode * Removed if statement. * Fix due to wrong assumption about the output of which. Checks if the which output contains a no match response, otherwise use gamemoderun. Using a case statement because it makes substring matching possible in sh and also it turns out that adding an empty string after env throws an error because env attempts to parse it as a paramater. * Missed a couple semicolons. * Different approach for checking if gamemode is available. Should hopefully work across all implementations of which. * Remove unneeded which command. * Change code to keep launch command to a single line. --- distribution/linux/Ryujinx.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/distribution/linux/Ryujinx.sh b/distribution/linux/Ryujinx.sh index 91fb467df..a80cdcaec 100644 --- a/distribution/linux/Ryujinx.sh +++ b/distribution/linux/Ryujinx.sh @@ -11,4 +11,10 @@ if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then RYUJINX_BIN="Ryujinx.Headless.SDL2" fi -env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/$RYUJINX_BIN" "$@" +COMMAND="env DOTNET_EnableAlternateStackCheck=1" + +if command -v gamemoderun > /dev/null 2>&1; then + COMMAND="$COMMAND gamemoderun" +fi + +$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@" From 1cf6d7b7bbcb4f10025e8230339873a15dea2752 Mon Sep 17 00:00:00 2001 From: Simon Wegendt Date: Mon, 29 May 2023 00:07:27 +0200 Subject: [PATCH 22/25] Fix #5108: Allow surround sound for SDL2 in more scenarios (#5131) --- src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index d3a73cfcb..7bfff5f9b 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Backends.SDL2 } else { - _supportSurroundConfiguration = spec.channels == 6; + _supportSurroundConfiguration = spec.channels >= 6; } } From 597388ecda35b0feaa3b678b7216689a5d84bbac Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 28 May 2023 19:17:07 -0300 Subject: [PATCH 23/25] Fix incorrect vertex attribute format change (#5112) * Fix incorrect vertex attribute format change * Only change vertex format if the host supports the new format --- .../FormatCapabilities.cs | 7 +++ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 1 - src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 50 +++++++++++++------ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index 7019dfd91..a2ebc518e 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -65,6 +65,13 @@ namespace Ryujinx.Graphics.Vulkan return (formatFeatureFlags & flags) == flags; } + public bool BufferFormatSupports(FormatFeatureFlags flags, VkFormat format) + { + _api.GetPhysicalDeviceFormatProperties(_physicalDevice, format, out var fp); + + return (fp.BufferFeatures & flags) == flags; + } + public bool OptimalFormatSupports(FormatFeatureFlags flags, GAL.Format format) { var formatFeatureFlags = _optimalTable[(int)format]; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index b4eccfbb2..c254d3781 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -551,7 +551,6 @@ namespace Ryujinx.Graphics.Vulkan (uint)maxDrawCount, (uint)stride); } - } else { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index aff3f13f0..1a396b5c8 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -414,7 +414,7 @@ namespace Ryujinx.Graphics.Vulkan if (isMoltenVk) { - UpdateVertexAttributeDescriptions(); + UpdateVertexAttributeDescriptions(gd); } fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0]) @@ -641,7 +641,7 @@ namespace Ryujinx.Graphics.Vulkan } } - private void UpdateVertexAttributeDescriptions() + private void UpdateVertexAttributeDescriptions(VulkanRenderer gd) { // Vertex attributes exceeding the stride are invalid. // In metal, they cause glitches with the vertex shader fetching incorrect values. @@ -651,30 +651,52 @@ namespace Ryujinx.Graphics.Vulkan for (int index = 0; index < VertexAttributeDescriptionsCount; index++) { var attribute = Internal.VertexAttributeDescriptions[index]; - ref var vb = ref Internal.VertexBindingDescriptions[(int)attribute.Binding]; + int vbIndex = GetVertexBufferIndex(attribute.Binding); - Format format = attribute.Format; - - while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride) + if (vbIndex >= 0) { - Format newFormat = FormatTable.DropLastComponent(format); + ref var vb = ref Internal.VertexBindingDescriptions[vbIndex]; - if (newFormat == format) + Format format = attribute.Format; + + while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride) { - // That case means we failed to find a format that fits within the stride, - // so just restore the original format and give up. - format = attribute.Format; - break; + Format newFormat = FormatTable.DropLastComponent(format); + + if (newFormat == format) + { + // That case means we failed to find a format that fits within the stride, + // so just restore the original format and give up. + format = attribute.Format; + break; + } + + format = newFormat; } - format = newFormat; + if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format)) + { + attribute.Format = format; + } } - attribute.Format = format; _vertexAttributeDescriptions2[index] = attribute; } } + private int GetVertexBufferIndex(uint binding) + { + for (int index = 0; index < VertexBindingDescriptionsCount; index++) + { + if (Internal.VertexBindingDescriptions[index].Binding == binding) + { + return index; + } + } + + return -1; + } + public void Dispose() { Stages.Dispose(); From 96d1f0da2d10545600d83a88e63cfef9b2785e25 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 28 May 2023 19:24:35 -0300 Subject: [PATCH 24/25] Workaround for MoltenVK barrier issues (#5118) --- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index c254d3781..688682f49 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -80,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan private PipelineColorBlendAttachmentState[] _storedBlend; + private ulong _drawCountSinceBarrier; public ulong DrawCount { get; private set; } public bool RenderPassActive { get; private set; } @@ -133,6 +134,18 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void Barrier() { + if (_drawCountSinceBarrier != DrawCount) + { + _drawCountSinceBarrier = DrawCount; + + // Barriers apparently have no effect inside a render pass on MoltenVK. + // As a workaround, end the render pass. + if (Gd.IsMoltenVk) + { + EndRenderPass(); + } + } + MemoryBarrier memoryBarrier = new MemoryBarrier() { SType = StructureType.MemoryBarrier, From 832a5e885281f6063f7855e92c3cf85eac82e715 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 28 May 2023 19:38:04 -0300 Subject: [PATCH 25/25] Make sure blend is disabled if render target has integer format (#5122) * Make sure blend is disabled if render target has integer format * Change approach to avoid permanently mutating state --- .../FramebufferParams.cs | 9 ++++++ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 1 + .../PipelineConverter.cs | 7 +++++ src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 31 +++++++++++++++++++ src/Ryujinx.Graphics.Vulkan/PipelineUid.cs | 1 + 5 files changed, 49 insertions(+) diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index cde992028..0437a402c 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Vulkan public uint[] AttachmentSamples { get; } public VkFormat[] AttachmentFormats { get; } public int[] AttachmentIndices { get; } + public uint AttachmentIntegerFormatMask { get; } public int AttachmentsCount { get; } public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1; @@ -74,6 +75,7 @@ namespace Ryujinx.Graphics.Vulkan int index = 0; int bindIndex = 0; + uint attachmentIntegerFormatMask = 0; foreach (ITexture color in colors) { @@ -89,6 +91,11 @@ namespace Ryujinx.Graphics.Vulkan AttachmentFormats[index] = texture.VkFormat; AttachmentIndices[index] = bindIndex; + if (texture.Info.Format.IsInteger()) + { + attachmentIntegerFormatMask |= 1u << bindIndex; + } + width = Math.Min(width, (uint)texture.Width); height = Math.Min(height, (uint)texture.Height); layers = Math.Min(layers, (uint)texture.Layers); @@ -102,6 +109,8 @@ namespace Ryujinx.Graphics.Vulkan bindIndex++; } + AttachmentIntegerFormatMask = attachmentIntegerFormatMask; + if (depthStencil is TextureView dsTexture && dsTexture.Valid) { _attachments[count - 1] = dsTexture.GetImageViewForAttachment(); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 688682f49..dcffa2473 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1487,6 +1487,7 @@ namespace Ryujinx.Graphics.Vulkan { var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); + _newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask; for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index da480d9f5..79179ce07 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -294,6 +294,7 @@ namespace Ryujinx.Graphics.Vulkan int attachmentCount = 0; int maxColorAttachmentIndex = -1; + uint attachmentIntegerFormatMask = 0; for (int i = 0; i < Constants.MaxRenderTargets; i++) { @@ -301,6 +302,11 @@ namespace Ryujinx.Graphics.Vulkan { pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); maxColorAttachmentIndex = i; + + if (state.AttachmentFormats[i].IsInteger()) + { + attachmentIntegerFormatMask |= 1u << i; + } } } @@ -311,6 +317,7 @@ namespace Ryujinx.Graphics.Vulkan pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); + pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask; return pipeline; } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index 1a396b5c8..7e803913f 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Silk.NET.Vulkan; using System; +using System.Numerics; namespace Ryujinx.Graphics.Vulkan { @@ -542,6 +543,27 @@ namespace Ryujinx.Graphics.Vulkan MaxDepthBounds = MaxDepthBounds }; + uint blendEnables = 0; + + if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0) + { + // Blend can't be enabled for integer formats, so let's make sure it is disabled. + uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask; + + while (attachmentIntegerFormatMask != 0) + { + int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask); + + if (Internal.ColorBlendAttachmentState[i].BlendEnable) + { + blendEnables |= 1u << i; + } + + Internal.ColorBlendAttachmentState[i].BlendEnable = false; + attachmentIntegerFormatMask &= ~(1u << i); + } + } + var colorBlendState = new PipelineColorBlendStateCreateInfo() { SType = StructureType.PipelineColorBlendStateCreateInfo, @@ -619,6 +641,15 @@ namespace Ryujinx.Graphics.Vulkan }; gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError(); + + // Restore previous blend enable values if we changed it. + while (blendEnables != 0) + { + int i = BitOperations.TrailingZeroCount(blendEnables); + + Internal.ColorBlendAttachmentState[i].BlendEnable = true; + blendEnables &= ~(1u << i); + } } pipeline = new Auto(new DisposablePipeline(gd.Api, device, pipelineHandle)); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs index 78d6e9f71..bf23f4714 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs @@ -35,6 +35,7 @@ namespace Ryujinx.Graphics.Vulkan public Array16 Scissors; public Array8 ColorBlendAttachmentState; public Array9 AttachmentFormats; + public uint AttachmentIntegerFormatMask; public override bool Equals(object obj) {