From 73f985d27ca0c85f053e8b9494ba83a6c4d3afbf Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:00:54 -0400 Subject: [PATCH 01/10] Replace passing by IMemoryOwner with passing by concrete MemoryOwner (#7171) * refactor(perf): pass MemoryOwner around as itself rather than IMemoryOwner * fix(perf): get span via MemoryOwner.Span property instead of through Memory property * fix: incorrect comment change --- src/Ryujinx.Graphics.GAL/ITexture.cs | 14 +++--- .../Commands/Texture/TextureSetDataCommand.cs | 6 +-- .../Texture/TextureSetDataSliceCommand.cs | 6 +-- .../TextureSetDataSliceRegionCommand.cs | 6 +-- .../Resources/ThreadedTexture.cs | 8 +-- .../Engine/Dma/DmaClass.cs | 4 +- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 49 +++++++++---------- .../Image/TextureGroup.cs | 4 +- .../Image/TextureBuffer.cs | 10 ++-- .../Image/TextureView.cs | 18 +++---- src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 10 ++-- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 14 +++--- .../HOS/Services/SurfaceFlinger/Parcel.cs | 2 +- src/Ryujinx.Memory/WritableRegion.cs | 6 +-- 14 files changed, 78 insertions(+), 79 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs index 2d9c6b7990..2aa4053ff2 100644 --- a/src/Ryujinx.Graphics.GAL/ITexture.cs +++ b/src/Ryujinx.Graphics.GAL/ITexture.cs @@ -1,4 +1,4 @@ -using System.Buffers; +using Ryujinx.Common.Memory; namespace Ryujinx.Graphics.GAL { @@ -18,30 +18,30 @@ namespace Ryujinx.Graphics.GAL PinnedSpan GetData(int layer, int level); /// - /// Sets the texture data. The data passed as a will be disposed when + /// Sets the texture data. The data passed as a will be disposed when /// the operation completes. /// /// Texture data bytes - void SetData(IMemoryOwner data); + void SetData(MemoryOwner data); /// - /// Sets the texture data. The data passed as a will be disposed when + /// Sets the texture data. The data passed as a will be disposed when /// the operation completes. /// /// Texture data bytes /// Target layer /// Target level - void SetData(IMemoryOwner data, int layer, int level); + void SetData(MemoryOwner data, int layer, int level); /// - /// Sets the texture data. The data passed as a will be disposed when + /// Sets the texture data. The data passed as a will be disposed when /// the operation completes. /// /// Texture data bytes /// Target layer /// Target level /// Target sub-region of the texture to update - void SetData(IMemoryOwner data, int layer, int level, Rectangle region); + void SetData(MemoryOwner data, int layer, int level, Rectangle region); void SetStorage(BufferRange buffer); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs index 3aba004dff..4449566a7e 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; -using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { @@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { public readonly CommandType CommandType => CommandType.TextureSetData; private TableRef _texture; - private TableRef> _data; + private TableRef> _data; - public void Set(TableRef texture, TableRef> data) + public void Set(TableRef texture, TableRef> data) { _texture = texture; _data = data; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs index 7ad709a757..3619149e9c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; -using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { @@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { public readonly CommandType CommandType => CommandType.TextureSetDataSlice; private TableRef _texture; - private TableRef> _data; + private TableRef> _data; private int _layer; private int _level; - public void Set(TableRef texture, TableRef> data, int layer, int level) + public void Set(TableRef texture, TableRef> data, int layer, int level) { _texture = texture; _data = data; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs index c211931bcc..6c6a536360 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; -using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { @@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion; private TableRef _texture; - private TableRef> _data; + private TableRef> _data; private int _layer; private int _level; private Rectangle _region; - public void Set(TableRef texture, TableRef> data, int layer, int level, Rectangle region) + public void Set(TableRef texture, TableRef> data, int layer, int level, Rectangle region) { _texture = texture; _data = data; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index 80003b8447..fa71d20b37 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; using Ryujinx.Graphics.GAL.Multithreading.Model; -using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Resources { @@ -111,21 +111,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } /// - public void SetData(IMemoryOwner data) + public void SetData(MemoryOwner data) { _renderer.New().Set(Ref(this), Ref(data)); _renderer.QueueCommand(); } /// - public void SetData(IMemoryOwner data, int layer, int level) + public void SetData(MemoryOwner data, int layer, int level) { _renderer.New().Set(Ref(this), Ref(data), layer, level); _renderer.QueueCommand(); } /// - public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) + public void SetData(MemoryOwner data, int layer, int level, Rectangle region) { _renderer.New().Set(Ref(this), Ref(data), layer, level, region); _renderer.QueueCommand(); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index f2bfd8eaa1..cdeae00409 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -1,10 +1,10 @@ using Ryujinx.Common; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Device; using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; using System; -using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -353,7 +353,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma if (target != null) { - IMemoryOwner data; + MemoryOwner data; if (srcLinear) { data = LayoutConverter.ConvertLinearStridedToLinear( diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 3b6c407cc2..7ee2e5cf0b 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -7,7 +7,6 @@ using Ryujinx.Graphics.Texture.Astc; using Ryujinx.Memory; using Ryujinx.Memory.Range; using System; -using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -662,7 +661,7 @@ namespace Ryujinx.Graphics.Gpu.Image } } - IMemoryOwner result = ConvertToHostCompatibleFormat(data); + MemoryOwner result = ConvertToHostCompatibleFormat(data); if (ScaleFactor != 1f && AllowScaledSetData()) { @@ -685,7 +684,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Uploads new texture data to the host GPU. /// /// New data - public void SetData(IMemoryOwner data) + public void SetData(MemoryOwner data) { BlacklistScale(); @@ -704,7 +703,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// New data /// Target layer /// Target level - public void SetData(IMemoryOwner data, int layer, int level) + public void SetData(MemoryOwner data, int layer, int level) { BlacklistScale(); @@ -722,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Target layer /// Target level /// Target sub-region of the texture to update - public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) + public void SetData(MemoryOwner data, int layer, int level, Rectangle region) { BlacklistScale(); @@ -740,7 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Mip level to convert /// True to convert a single slice /// Converted data - public IMemoryOwner ConvertToHostCompatibleFormat(ReadOnlySpan data, int level = 0, bool single = false) + public MemoryOwner ConvertToHostCompatibleFormat(ReadOnlySpan data, int level = 0, bool single = false) { int width = Info.Width; int height = Info.Height; @@ -755,7 +754,7 @@ namespace Ryujinx.Graphics.Gpu.Image int sliceDepth = single ? 1 : depth; - IMemoryOwner linear; + MemoryOwner linear; if (Info.IsLinear) { @@ -788,7 +787,7 @@ namespace Ryujinx.Graphics.Gpu.Image data); } - IMemoryOwner result = linear; + MemoryOwner result = linear; // Handle compressed cases not supported by the host: // - ASTC is usually not supported on desktop cards. @@ -832,19 +831,19 @@ namespace Ryujinx.Graphics.Gpu.Image case Format.Etc2RgbaUnorm: using (result) { - return ETC2Decoder.DecodeRgba(result.Memory.Span, width, height, sliceDepth, levels, layers); + return ETC2Decoder.DecodeRgba(result.Span, width, height, sliceDepth, levels, layers); } case Format.Etc2RgbPtaSrgb: case Format.Etc2RgbPtaUnorm: using (result) { - return ETC2Decoder.DecodePta(result.Memory.Span, width, height, sliceDepth, levels, layers); + return ETC2Decoder.DecodePta(result.Span, width, height, sliceDepth, levels, layers); } case Format.Etc2RgbSrgb: case Format.Etc2RgbUnorm: using (result) { - return ETC2Decoder.DecodeRgb(result.Memory.Span, width, height, sliceDepth, levels, layers); + return ETC2Decoder.DecodeRgb(result.Span, width, height, sliceDepth, levels, layers); } } } @@ -856,43 +855,43 @@ namespace Ryujinx.Graphics.Gpu.Image case Format.Bc1RgbaUnorm: using (result) { - return BCnDecoder.DecodeBC1(result.Memory.Span, width, height, sliceDepth, levels, layers); + return BCnDecoder.DecodeBC1(result.Span, width, height, sliceDepth, levels, layers); } case Format.Bc2Srgb: case Format.Bc2Unorm: using (result) { - return BCnDecoder.DecodeBC2(result.Memory.Span, width, height, sliceDepth, levels, layers); + return BCnDecoder.DecodeBC2(result.Span, width, height, sliceDepth, levels, layers); } case Format.Bc3Srgb: case Format.Bc3Unorm: using (result) { - return BCnDecoder.DecodeBC3(result.Memory.Span, width, height, sliceDepth, levels, layers); + return BCnDecoder.DecodeBC3(result.Span, width, height, sliceDepth, levels, layers); } case Format.Bc4Snorm: case Format.Bc4Unorm: using (result) { - return BCnDecoder.DecodeBC4(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm); + return BCnDecoder.DecodeBC4(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm); } case Format.Bc5Snorm: case Format.Bc5Unorm: using (result) { - return BCnDecoder.DecodeBC5(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm); + return BCnDecoder.DecodeBC5(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm); } case Format.Bc6HSfloat: case Format.Bc6HUfloat: using (result) { - return BCnDecoder.DecodeBC6(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat); + return BCnDecoder.DecodeBC6(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat); } case Format.Bc7Srgb: case Format.Bc7Unorm: using (result) { - return BCnDecoder.DecodeBC7(result.Memory.Span, width, height, sliceDepth, levels, layers); + return BCnDecoder.DecodeBC7(result.Span, width, height, sliceDepth, levels, layers); } } } @@ -900,7 +899,7 @@ namespace Ryujinx.Graphics.Gpu.Image { using (result) { - var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Memory.Span, width); + var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Span, width); if (_context.Capabilities.SupportsR4G4B4A4Format) { @@ -910,7 +909,7 @@ namespace Ryujinx.Graphics.Gpu.Image { using (converted) { - return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Memory.Span, width); + return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Span, width); } } } @@ -921,7 +920,7 @@ namespace Ryujinx.Graphics.Gpu.Image { using (result) { - return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width); + return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Span, width); } } } @@ -933,24 +932,24 @@ namespace Ryujinx.Graphics.Gpu.Image case Format.R5G6B5Unorm: using (result) { - return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Memory.Span, width); + return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Span, width); } case Format.B5G5R5A1Unorm: case Format.R5G5B5X1Unorm: case Format.R5G5B5A1Unorm: using (result) { - return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Memory.Span, width, Format == Format.R5G5B5X1Unorm); + return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Span, width, Format == Format.R5G5B5X1Unorm); } case Format.A1B5G5R5Unorm: using (result) { - return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Memory.Span, width); + return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Span, width); } case Format.R4G4B4A4Unorm: using (result) { - return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width); + return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Span, width); } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 06ca2c5997..526fc0c246 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; @@ -5,7 +6,6 @@ using Ryujinx.Memory; using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; using System; -using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image ReadOnlySpan data = dataSpan[(offset - spanBase)..]; - IMemoryOwner result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); + MemoryOwner result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level); } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index a8196541a1..22f4c04cde 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -1,7 +1,7 @@ using OpenTK.Graphics.OpenGL; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using System; -using System.Buffers; namespace Ryujinx.Graphics.OpenGL.Image { @@ -55,9 +55,9 @@ namespace Ryujinx.Graphics.OpenGL.Image } /// - public void SetData(IMemoryOwner data) + public void SetData(MemoryOwner data) { - var dataSpan = data.Memory.Span; + var dataSpan = data.Span; Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]); @@ -65,13 +65,13 @@ namespace Ryujinx.Graphics.OpenGL.Image } /// - public void SetData(IMemoryOwner data, int layer, int level) + public void SetData(MemoryOwner data, int layer, int level) { throw new NotSupportedException(); } /// - public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) + public void SetData(MemoryOwner data, int layer, int level, Rectangle region) { throw new NotSupportedException(); } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 946eb755cc..b0859c49e2 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -1,8 +1,8 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using System; -using System.Buffers; using System.Diagnostics; namespace Ryujinx.Graphics.OpenGL.Image @@ -448,13 +448,13 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - public void SetData(IMemoryOwner data) + public void SetData(MemoryOwner data) { using (data = EnsureDataFormat(data)) { unsafe { - var dataSpan = data.Memory.Span; + var dataSpan = data.Span; fixed (byte* ptr = dataSpan) { ReadFrom((IntPtr)ptr, dataSpan.Length); @@ -463,13 +463,13 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - public void SetData(IMemoryOwner data, int layer, int level) + public void SetData(MemoryOwner data, int layer, int level) { using (data = EnsureDataFormat(data)) { unsafe { - fixed (byte* ptr = data.Memory.Span) + fixed (byte* ptr = data.Span) { int width = Math.Max(Info.Width >> level, 1); int height = Math.Max(Info.Height >> level, 1); @@ -480,7 +480,7 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) + public void SetData(MemoryOwner data, int layer, int level, Rectangle region) { using (data = EnsureDataFormat(data)) { @@ -489,7 +489,7 @@ namespace Ryujinx.Graphics.OpenGL.Image unsafe { - fixed (byte* ptr = data.Memory.Span) + fixed (byte* ptr = data.Span) { ReadFrom2D( (IntPtr)ptr, @@ -522,13 +522,13 @@ namespace Ryujinx.Graphics.OpenGL.Image ReadFrom2D(data, layer, level, x, y, width, height, mipSize); } - private IMemoryOwner EnsureDataFormat(IMemoryOwner data) + private MemoryOwner EnsureDataFormat(MemoryOwner data) { if (Format == Format.S8UintD24Unorm) { using (data) { - return FormatConverter.ConvertS8D24ToD24S8(data.Memory.Span); + return FormatConverter.ConvertS8D24ToD24S8(data.Span); } } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index bc1a509616..073eee2ca9 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -1,7 +1,7 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; -using System.Buffers; using System.Collections.Generic; using Format = Ryujinx.Graphics.GAL.Format; using VkFormat = Silk.NET.Vulkan.Format; @@ -84,20 +84,20 @@ namespace Ryujinx.Graphics.Vulkan } /// - public void SetData(IMemoryOwner data) + public void SetData(MemoryOwner data) { - _gd.SetBufferData(_bufferHandle, _offset, data.Memory.Span); + _gd.SetBufferData(_bufferHandle, _offset, data.Span); data.Dispose(); } /// - public void SetData(IMemoryOwner data, int layer, int level) + public void SetData(MemoryOwner data, int layer, int level) { throw new NotSupportedException(); } /// - public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) + public void SetData(MemoryOwner data, int layer, int level, Rectangle region) { throw new NotSupportedException(); } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index 9b3f466627..b7b936809d 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -1,7 +1,7 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; -using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -746,23 +746,23 @@ namespace Ryujinx.Graphics.Vulkan } /// - public void SetData(IMemoryOwner data) + public void SetData(MemoryOwner data) { - SetData(data.Memory.Span, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false); + SetData(data.Span, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false); data.Dispose(); } /// - public void SetData(IMemoryOwner data, int layer, int level) + public void SetData(MemoryOwner data, int layer, int level) { - SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true); + SetData(data.Span, layer, level, 1, 1, singleSlice: true); data.Dispose(); } /// - public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) + public void SetData(MemoryOwner data, int layer, int level, Rectangle region) { - SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true, region); + SetData(data.Span, layer, level, 1, 1, singleSlice: true, region); data.Dispose(); } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs index 2ca0e1aac2..1df280dce4 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { private readonly MemoryOwner _rawDataOwner; - private Span Raw => _rawDataOwner.Memory.Span; + private Span Raw => _rawDataOwner.Span; private ref ParcelHeader Header => ref MemoryMarshal.Cast(Raw)[0]; diff --git a/src/Ryujinx.Memory/WritableRegion.cs b/src/Ryujinx.Memory/WritableRegion.cs index 2c21ef4e80..54facb5085 100644 --- a/src/Ryujinx.Memory/WritableRegion.cs +++ b/src/Ryujinx.Memory/WritableRegion.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Memory; using System; -using System.Buffers; namespace Ryujinx.Memory { @@ -7,7 +7,7 @@ namespace Ryujinx.Memory { private readonly IWritableBlock _block; private readonly ulong _va; - private readonly IMemoryOwner _memoryOwner; + private readonly MemoryOwner _memoryOwner; private readonly bool _tracked; private bool NeedsWriteback => _block != null; @@ -22,7 +22,7 @@ namespace Ryujinx.Memory Memory = memory; } - public WritableRegion(IWritableBlock block, ulong va, IMemoryOwner memoryOwner, bool tracked = false) + public WritableRegion(IWritableBlock block, ulong va, MemoryOwner memoryOwner, bool tracked = false) : this(block, va, memoryOwner.Memory, tracked) { _memoryOwner = memoryOwner; From 24ee8c39f1fd8ae2dc2d92cda1cdb41e8af45f0a Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 19 Sep 2024 14:38:30 -0300 Subject: [PATCH 02/10] Add support for sampler sRGB disable (#7312) --- src/Ryujinx.Graphics.Gpu/Image/Sampler.cs | 7 +++++ .../Image/SamplerDescriptor.cs | 9 ++++++ .../Image/TextureBindingsManager.cs | 10 ++++--- src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 28 +++++++++++++++++-- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs index d6a3d975b8..b007c15910 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs @@ -13,6 +13,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// public bool IsDisposed { get; private set; } + /// + /// True if the sampler has sRGB conversion enabled, false otherwise. + /// + public bool IsSrgb { get; } + /// /// Host sampler object. /// @@ -30,6 +35,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// The Maxwell sampler descriptor public Sampler(GpuContext context, SamplerDescriptor descriptor) { + IsSrgb = descriptor.UnpackSrgb(); + MinFilter minFilter = descriptor.UnpackMinFilter(); MagFilter magFilter = descriptor.UnpackMagFilter(); diff --git a/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs index e04c31dfaf..836a3260c6 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs @@ -113,6 +113,15 @@ namespace Ryujinx.Graphics.Gpu.Image return (CompareOp)(((Word0 >> 10) & 7) + 1); } + /// + /// Unpacks the sampler sRGB format flag. + /// + /// True if the has sampler is sRGB conversion enabled, false otherwise + public readonly bool UnpackSrgb() + { + return (Word0 & (1 << 13)) != 0; + } + /// /// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering. /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index ad018f1597..f96ddfb1bf 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -187,7 +187,9 @@ namespace Ryujinx.Graphics.Gpu.Image { (TexturePool texturePool, SamplerPool samplerPool) = GetPools(); - return (texturePool.Get(textureId), samplerPool.Get(samplerId)); + Sampler sampler = samplerPool?.Get(samplerId); + + return (texturePool.Get(textureId, sampler?.IsSrgb ?? true), sampler); } /// @@ -508,12 +510,12 @@ namespace Ryujinx.Graphics.Gpu.Image state.TextureHandle = textureId; state.SamplerHandle = samplerId; - ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture); + Sampler sampler = samplerPool?.Get(samplerId); + + ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, sampler?.IsSrgb ?? true, out Texture texture); specStateMatches &= specState.MatchesTexture(stage, index, descriptor); - Sampler sampler = samplerPool?.Get(samplerId); - ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); ISampler hostSampler = sampler?.GetHostSampler(texture); diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 5f43c1824e..be7cb0b893 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -227,6 +227,17 @@ namespace Ryujinx.Graphics.Gpu.Image /// ID of the texture. This is effectively a zero-based index /// The texture with the given ID public override Texture Get(int id) + { + return Get(id, srgbSampler: true); + } + + /// + /// Gets the texture with the given ID. + /// + /// ID of the texture. This is effectively a zero-based index + /// Whether the texture is being accessed with a sampler that has sRGB conversion enabled + /// The texture with the given ID + public Texture Get(int id, bool srgbSampler) { if ((uint)id >= Items.Length) { @@ -240,7 +251,7 @@ namespace Ryujinx.Graphics.Gpu.Image SynchronizeMemory(); } - GetInternal(id, out Texture texture); + GetForBinding(id, srgbSampler, out Texture texture); return texture; } @@ -252,9 +263,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// This method assumes that the pool has been manually synchronized before doing binding. /// /// ID of the texture. This is effectively a zero-based index + /// Whether the texture is being accessed with a sampler that has sRGB conversion enabled /// The texture with the given ID /// The texture descriptor with the given ID - public ref readonly TextureDescriptor GetForBinding(int id, out Texture texture) + public ref readonly TextureDescriptor GetForBinding(int id, bool srgbSampler, out Texture texture) { if ((uint)id >= Items.Length) { @@ -264,6 +276,18 @@ namespace Ryujinx.Graphics.Gpu.Image // When getting for binding, assume the pool has already been synchronized. + if (!srgbSampler) + { + // If the sampler does not have the sRGB bit enabled, then the texture can't use a sRGB format. + ref readonly TextureDescriptor tempDescriptor = ref GetDescriptorRef(id); + + if (tempDescriptor.UnpackSrgb() && FormatTable.TryGetTextureFormat(tempDescriptor.UnpackFormat(), isSrgb: false, out FormatInfo formatInfo)) + { + // Get a view of the texture with the right format. + return ref GetForBinding(id, formatInfo, out texture); + } + } + return ref GetInternal(id, out texture); } From d717aef2be6043ef5a5f064e13db444ef873f6bb Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 19 Sep 2024 21:23:09 -0300 Subject: [PATCH 03/10] Shader: Assume the only remaining source is the right one when all others are undefined (#7331) * Shader: Assume the only remaining source is the right one when all other are undefined * Shader cache version bump * Improve comment --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Translation/Optimizations/Utils.cs | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index e1e696ca86..a5c5abd4bb 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 = 7320; + private const uint CodeGenVersion = 7331; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs index 23180ff825..6ec90fa3c5 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs @@ -138,6 +138,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // Ensure that conditions met for that branch are also met for the current one. // Prefer the latest sources for the phi node. + int undefCount = 0; + for (int i = phiNode.SourcesCount - 1; i >= 0; i--) { BasicBlock phiBlock = phiNode.GetBlock(i); @@ -159,6 +161,26 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return match; } } + else if (phiSource.Type == OperandType.Undefined) + { + undefCount++; + } + } + + // If all sources but one are undefined, we can assume that the one + // that is not undefined is the right one. + + if (undefCount == phiNode.SourcesCount - 1) + { + for (int i = phiNode.SourcesCount - 1; i >= 0; i--) + { + Operand phiSource = phiNode.GetSource(i); + + if (phiSource.Type != OperandType.Undefined) + { + return phiSource; + } + } } } From 319507f2a12a6751f3ab833e498a3efd3119f806 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 22 Sep 2024 19:36:53 -0300 Subject: [PATCH 04/10] Fix quads draws after DrawTexture on Vulkan (#7336) --- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 86fab760f1..addad83fd5 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -636,9 +636,9 @@ namespace Ryujinx.Graphics.Vulkan var oldStencilTestEnable = _newState.StencilTestEnable; var oldDepthTestEnable = _newState.DepthTestEnable; var oldDepthWriteEnable = _newState.DepthWriteEnable; - var oldTopology = _newState.Topology; var oldViewports = DynamicState.Viewports; var oldViewportsCount = _newState.ViewportsCount; + var oldTopology = _topology; _newState.CullMode = CullModeFlags.None; _newState.StencilTestEnable = false; @@ -658,7 +658,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.StencilTestEnable = oldStencilTestEnable; _newState.DepthTestEnable = oldDepthTestEnable; _newState.DepthWriteEnable = oldDepthWriteEnable; - _newState.Topology = oldTopology; + SetPrimitiveTopology(oldTopology); DynamicState.SetViewports(ref oldViewports, oldViewportsCount); From 050f22977f7dc0ec193bf67c7ee66625abf36e5c Mon Sep 17 00:00:00 2001 From: Jason Youngberg <71162172+jasondyoungberg@users.noreply.github.com> Date: Tue, 24 Sep 2024 03:10:36 -0600 Subject: [PATCH 05/10] Update bug_report.yml to provide better instructions for finding log file (#7333) --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 68be1f5e0d..ffb5d5f8bd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -23,7 +23,7 @@ body: attributes: label: Log file description: A log file will help our developers to better diagnose and fix the issue. - placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area + placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste). validations: required: true - type: input @@ -83,4 +83,4 @@ body: - Additional info about your environment: - Any other information relevant to your issue. validations: - required: false \ No newline at end of file + required: false From 04d68ca6168326f798f9acfa6a273f31ac53f3c6 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 26 Sep 2024 18:19:12 +0100 Subject: [PATCH 06/10] GPU: Ensure all clip distances are initialized when used (#7363) * GPU: Ensure all clip distances are initialized when used * Shader cache version --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Translation/Translator.cs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index a5c5abd4bb..c36fc0ada2 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 = 7331; + private const uint CodeGenVersion = 7353; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index 6a31ea2e79..d1fbca0eb0 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (stage == ShaderStage.Vertex) { - InitializePositionOutput(context); + InitializeVertexOutputs(context); } UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents; @@ -236,12 +236,20 @@ namespace Ryujinx.Graphics.Shader.Translation } } - private static void InitializePositionOutput(EmitterContext context) + private static void InitializeVertexOutputs(EmitterContext context) { for (int c = 0; c < 4; c++) { context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f)); } + + if (context.Program.ClipDistancesWritten != 0) + { + for (int i = 0; i < 8; i++) + { + context.Store(StorageKind.Output, IoVariable.ClipDistance, null, Const(i), ConstF(0f)); + } + } } private static void InitializeOutput(EmitterContext context, int location, bool perPatch) From d86249cb0add1504c0d3a5c41527cbef43346742 Mon Sep 17 00:00:00 2001 From: MaxLastBreath <136052075+MaxLastBreath@users.noreply.github.com> Date: Thu, 26 Sep 2024 20:33:38 +0300 Subject: [PATCH 07/10] Convert MaxTextureCacheCapacity to Dynamic MaxTextureCacheCapacity for High Resolution Mod support. (#7307) * Add Texture Size Capacity and 8GB Dram Build * Update AutoDeleteCache.cs * Dynamic Texture Cache (WIP) * Change to float Multiplier, in-case it needs fine-tuning. * Delete src/src.sln * Update AutoDeleteCache.cs * Format * Fix Formatting * Add DefaultTextureSizeCapacity and MemoryScaleFactor - Also remove redundant New Lines * Fix 4GB dram crashing * Format newline * Refractor - Added Initialize() function to TextureCache and AutoDeleteCache - Removed GetMaxTextureCapacity() function and instead added _maxCacheMemoryUsage - Added private const MaxTextureSizeCapacity to AutoDelete Cache - Added TextureCache.Initialize() to MemoryManager in order to fetch MaxGpuMemory at the right time. - Moved and Changed Logger.Info for Gpu Memory to Logger.Notice and Moved it to PrintGpuInformation function. - Opted to use a ternary operator for the Initialize function, I think it looks cleaner than bunch of if statements. * Update src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs Co-authored-by: gdkchan * maxMemory to CacheMemory, use Clamp instead of Ternary. Changed MinTextureCapacity 1GiB to 512 MiB * Update src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs Co-authored-by: gdkchan * Format comment * comment context * Increase TextureSize capacity for OpenGL back to 1024 - Added a new const ulong for OpenGLTextureSizeCapacity * Fix changes from last commit. * Adjust last OpenGL changes. * Remove garbage VSC file * Update src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs Co-authored-by: gdkchan * Update src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs Co-authored-by: gdkchan * Update src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs Co-authored-by: gdkchan --------- Co-authored-by: gdkchan --- src/Ryujinx.Graphics.GAL/Capabilities.cs | 6 +++- .../Image/AutoDeleteCache.cs | 30 +++++++++++++++++-- .../Image/TextureCache.cs | 8 +++++ .../Memory/MemoryManager.cs | 2 ++ src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 3 +- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 22 +++++++++++++- 6 files changed, 65 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index a5c6eb5c8e..1eec80e51b 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -71,6 +71,8 @@ namespace Ryujinx.Graphics.GAL public readonly int GatherBiasPrecision; + public readonly ulong MaximumGpuMemory; + public Capabilities( TargetApi api, string vendorName, @@ -131,7 +133,8 @@ namespace Ryujinx.Graphics.GAL int shaderSubgroupSize, int storageBufferOffsetAlignment, int textureBufferOffsetAlignment, - int gatherBiasPrecision) + int gatherBiasPrecision, + ulong maximumGpuMemory) { Api = api; VendorName = vendorName; @@ -193,6 +196,7 @@ namespace Ryujinx.Graphics.GAL StorageBufferOffsetAlignment = storageBufferOffsetAlignment; TextureBufferOffsetAlignment = textureBufferOffsetAlignment; GatherBiasPrecision = gatherBiasPrecision; + MaximumGpuMemory = maximumGpuMemory; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index 5e66a3b543..ad6c1fecb2 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; @@ -46,7 +47,11 @@ namespace Ryujinx.Graphics.Gpu.Image { private const int MinCountForDeletion = 32; private const int MaxCapacity = 2048; - private const ulong MaxTextureSizeCapacity = 1024 * 1024 * 1024; // MB; + private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024; + private const ulong MaxTextureSizeCapacity = 4UL * 1024 * 1024 * 1024; + private const ulong DefaultTextureSizeCapacity = 1UL * 1024 * 1024 * 1024; + private const float MemoryScaleFactor = 0.50f; + private ulong _maxCacheMemoryUsage = 0; private readonly LinkedList _textures; private ulong _totalSize; @@ -56,6 +61,25 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly Dictionary _shortCacheLookup; + /// + /// Initializes the cache, setting the maximum texture capacity for the specified GPU context. + /// + /// + /// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`. + /// + /// The GPU context that the cache belongs to + public void Initialize(GpuContext context) + { + var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor); + + _maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity); + + if (context.Capabilities.MaximumGpuMemory == 0) + { + _maxCacheMemoryUsage = DefaultTextureSizeCapacity; + } + } + /// /// Creates a new instance of the automatic deletion cache. /// @@ -85,7 +109,7 @@ namespace Ryujinx.Graphics.Gpu.Image texture.CacheNode = _textures.AddLast(texture); if (_textures.Count > MaxCapacity || - (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)) + (_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion)) { RemoveLeastUsedTexture(); } @@ -110,7 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Image _textures.AddLast(texture.CacheNode); } - if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion) + if (_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion) { RemoveLeastUsedTexture(); } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 5a3319b06a..1587e20189 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -68,6 +68,14 @@ namespace Ryujinx.Graphics.Gpu.Image _cache = new AutoDeleteCache(); } + /// + /// Initializes the cache, setting the maximum texture capacity for the specified GPU context. + /// + public void Initialize() + { + _cache.Initialize(_context); + } + /// /// Handles marking of textures written to a memory region being (partially) remapped. /// diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 59a940a4f9..d1065431d1 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Memory; using Ryujinx.Memory.Range; using System; @@ -64,6 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Memory MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler; MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler; MemoryUnmapped += CounterCache.MemoryUnmappedHandler; + Physical.TextureCache.Initialize(); } /// diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index ba9cd45c67..9fcdf1ad79 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -202,7 +202,8 @@ namespace Ryujinx.Graphics.OpenGL shaderSubgroupSize: Constants.MaxSubgroupSize, storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment, textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment, - gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan. + gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0, // Precision is 8 for these vendors on Vulkan. + maximumGpuMemory: 0); } public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 33e41ab489..0faaec82a4 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -781,7 +781,26 @@ namespace Ryujinx.Graphics.Vulkan shaderSubgroupSize: (int)Capabilities.SubgroupSize, storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment, textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment, - gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0); + gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0, + maximumGpuMemory: GetTotalGPUMemory()); + } + + private ulong GetTotalGPUMemory() + { + ulong totalMemory = 0; + + Api.GetPhysicalDeviceMemoryProperties(_physicalDevice.PhysicalDevice, out PhysicalDeviceMemoryProperties memoryProperties); + + for (int i = 0; i < memoryProperties.MemoryHeapCount; i++) + { + var heap = memoryProperties.MemoryHeaps[i]; + if ((heap.Flags & MemoryHeapFlags.DeviceLocalBit) == MemoryHeapFlags.DeviceLocalBit) + { + totalMemory += heap.Size; + } + } + + return totalMemory; } public HardwareInfo GetHardwareInfo() @@ -865,6 +884,7 @@ namespace Ryujinx.Graphics.Vulkan private void PrintGpuInformation() { Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); + Logger.Notice.Print(LogClass.Gpu, $"GPU Memory: {GetTotalGPUMemory() / (1024 * 1024)} MiB"); } public void Initialize(GraphicsDebugLevel logLevel) From 5dbba07e33e83c9047dcbb701c9655edbbe89086 Mon Sep 17 00:00:00 2001 From: e2dk4r <43293320+e2dk4r@users.noreply.github.com> Date: Sat, 28 Sep 2024 11:44:23 +0300 Subject: [PATCH 08/10] sdl: set app name (#7370) Ryujinx was not hinting application name, so on some platforms (e.g. Linux) volume control shows Ryujinx as 'SDL Application'. This can cause confusion. This commit fixes name in volume control applets on some platforms. see: https://wiki.libsdl.org/SDL2/SDL_HINT_APP_NAME --- src/Ryujinx.SDL2.Common/SDL2Driver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index ed6d941908..9827156d03 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -53,6 +53,7 @@ namespace Ryujinx.SDL2.Common return; } + SDL_SetHint(SDL_HINT_APP_NAME, "Ryujinx"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); From 7d158acc3b5826a08941d6e8d50d3a3897021bcd Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 30 Sep 2024 11:41:07 -0300 Subject: [PATCH 09/10] Do not try to create a texture pool if shader does not use textures (#7379) --- src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 98acb6f27d..1230c05805 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -743,7 +743,7 @@ namespace Ryujinx.Graphics.Gpu.Shader constantBufferUsePerStageMask &= ~(1 << index); } - if (checkTextures) + if (checkTextures && _allTextures.Length > 0) { TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId); From a2c003501371463fd1f98d2e5a7602ae19c21d7c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 1 Oct 2024 07:30:57 -0300 Subject: [PATCH 10/10] Update audio renderer to REV13: Add support for compressor statistics and volume reset (#7372) * Update audio renderer to REV13: Add support for compressor statistics and volume reset * XML docs * Disable stats reset * Wrong comment * Fix more XML docs * PR feedback --- .../Renderer/Dsp/Command/CompressorCommand.cs | 45 +++++++++++---- .../Dsp/Command/LimiterCommandVersion1.cs | 46 +++++++-------- .../Dsp/Command/LimiterCommandVersion2.cs | 48 ++++++++-------- .../Parameter/Effect/CompressorParameter.cs | 11 +++- .../Parameter/Effect/CompressorStatistics.cs | 38 +++++++++++++ .../ISplitterDestinationInParameter.cs | 5 ++ .../SplitterDestinationInParameterVersion1.cs | 9 ++- .../SplitterDestinationInParameterVersion2.cs | 9 ++- .../Renderer/Server/BehaviourContext.cs | 19 ++++++- .../Renderer/Server/CommandBuffer.cs | 13 ++++- .../Renderer/Server/CommandGenerator.cs | 16 +++++- .../CommandProcessingTimeEstimatorVersion5.cs | 56 ++++++++++++++----- .../Server/Effect/CompressorEffect.cs | 13 +++++ .../Server/Splitter/SplitterContext.cs | 9 ++- .../Server/Splitter/SplitterDestination.cs | 7 ++- .../Splitter/SplitterDestinationVersion1.cs | 6 +- .../Splitter/SplitterDestinationVersion2.cs | 6 +- src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs | 1 + .../Sdk/Audio/Detail/AudioDevice.cs | 42 ++++++++++++++ .../Renderer/Server/BehaviourContextTests.cs | 41 ++++++++++++++ 20 files changed, 352 insertions(+), 88 deletions(-) create mode 100644 src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorStatistics.cs diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs index 09f415d20c..33f61e6a51 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs @@ -1,9 +1,11 @@ using Ryujinx.Audio.Renderer.Dsp.Effect; using Ryujinx.Audio.Renderer.Dsp.State; +using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Server.Effect; using System; using System.Diagnostics; +using System.Runtime.InteropServices; namespace Ryujinx.Audio.Renderer.Dsp.Command { @@ -21,18 +23,20 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CompressorParameter Parameter => _parameter; public Memory State { get; } + public Memory ResultState { get; } public ushort[] OutputBufferIndices { get; } public ushort[] InputBufferIndices { get; } public bool IsEffectEnabled { get; } private CompressorParameter _parameter; - public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory state, bool isEnabled, int nodeId) + public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory state, Memory resultState, bool isEnabled, int nodeId) { Enabled = true; NodeId = nodeId; _parameter = parameter; State = state; + ResultState = resultState; IsEffectEnabled = isEnabled; @@ -71,9 +75,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled && _parameter.IsChannelCountValid()) { - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span channelInput = stackalloc float[Parameter.ChannelCount]; + if (!ResultState.IsEmpty && _parameter.StatisticsReset) + { + ref CompressorStatistics statistics = ref MemoryMarshal.Cast(ResultState.Span[0].SpecificData)[0]; + + statistics.Reset(_parameter.ChannelCount); + } + + Span inputBuffers = stackalloc IntPtr[_parameter.ChannelCount]; + Span outputBuffers = stackalloc IntPtr[_parameter.ChannelCount]; + Span channelInput = stackalloc float[_parameter.ChannelCount]; ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage; float unknown4 = state.Unknown4; ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage; @@ -92,7 +103,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex); } - float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain); + float mean = FloatingPointHelper.MeanSquare(channelInput); + float newMean = inputMovingAverage.Update(mean, _parameter.InputGain); float y = FloatingPointHelper.Log10(newMean) * 10.0f; float z = 1.0f; @@ -111,7 +123,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (y >= state.Unknown14) { - tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold); + tmpGain = ((1.0f / _parameter.Ratio) - 1.0f) * (y - _parameter.Threshold); } else { @@ -126,7 +138,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if ((unknown4 - z) <= 0.08f) { - compressionEmaAlpha = Parameter.ReleaseCoefficient; + compressionEmaAlpha = _parameter.ReleaseCoefficient; if ((unknown4 - z) >= -0.08f) { @@ -140,18 +152,31 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } else { - compressionEmaAlpha = Parameter.AttackCoefficient; + compressionEmaAlpha = _parameter.AttackCoefficient; } float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha); - for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++) + for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) { *((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain; } unknown4 = unknown4New; previousCompressionEmaAlpha = compressionEmaAlpha; + + if (!ResultState.IsEmpty) + { + ref CompressorStatistics statistics = ref MemoryMarshal.Cast(ResultState.Span[0].SpecificData)[0]; + + statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain); + statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean); + + for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) + { + statistics.LastSamples[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f)); + } + } } state.InputMovingAverage = inputMovingAverage; @@ -161,7 +186,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } else { - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { if (InputBufferIndices[i] != OutputBufferIndices[i]) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs index 3ba0b5884d..06e9321997 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs @@ -38,10 +38,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { - InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]); - OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]); + InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]); } } @@ -51,11 +51,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled) { - if (Parameter.Status == UsageState.Invalid) + if (_parameter.Status == UsageState.Invalid) { state = new LimiterState(ref _parameter, WorkBuffer); } - else if (Parameter.Status == UsageState.New) + else if (_parameter.Status == UsageState.New) { LimiterState.UpdateParameter(ref _parameter); } @@ -66,56 +66,56 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command private unsafe void ProcessLimiter(CommandList context, ref LimiterState state) { - Debug.Assert(Parameter.IsChannelCountValid()); + Debug.Assert(_parameter.IsChannelCountValid()); - if (IsEffectEnabled && Parameter.IsChannelCountValid()) + if (IsEffectEnabled && _parameter.IsChannelCountValid()) { - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; + Span inputBuffers = stackalloc IntPtr[_parameter.ChannelCount]; + Span outputBuffers = stackalloc IntPtr[_parameter.ChannelCount]; - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]); outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]); } - for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++) + for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) { for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++) { float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex); - float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain; + float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain; float sampleInputMax = Math.Abs(inputSample); - float inputCoefficient = Parameter.ReleaseCoefficient; + float inputCoefficient = _parameter.ReleaseCoefficient; if (sampleInputMax > state.DetectorAverage[channelIndex].Read()) { - inputCoefficient = Parameter.AttackCoefficient; + inputCoefficient = _parameter.AttackCoefficient; } float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient); float attenuation = 1.0f; - if (detectorValue > Parameter.Threshold) + if (detectorValue > _parameter.Threshold) { - attenuation = Parameter.Threshold / detectorValue; + attenuation = _parameter.Threshold / detectorValue; } - float outputCoefficient = Parameter.ReleaseCoefficient; + float outputCoefficient = _parameter.ReleaseCoefficient; if (state.CompressionGainAverage[channelIndex].Read() > attenuation) { - outputCoefficient = Parameter.AttackCoefficient; + outputCoefficient = _parameter.AttackCoefficient; } float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient); - ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; + ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; - float outputSample = delayedSample * compressionGain * Parameter.OutputGain; + float outputSample = delayedSample * compressionGain * _parameter.OutputGain; *((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue; @@ -123,16 +123,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command state.DelayedSampleBufferPosition[channelIndex]++; - while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin) + while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin) { - state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin; + state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin; } } } } else { - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { if (InputBufferIndices[i] != OutputBufferIndices[i]) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs index f6e1654dd3..ed0538c061 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs @@ -49,10 +49,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { - InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]); - OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]); + InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]); } } @@ -62,11 +62,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled) { - if (Parameter.Status == UsageState.Invalid) + if (_parameter.Status == UsageState.Invalid) { state = new LimiterState(ref _parameter, WorkBuffer); } - else if (Parameter.Status == UsageState.New) + else if (_parameter.Status == UsageState.New) { LimiterState.UpdateParameter(ref _parameter); } @@ -77,63 +77,63 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command private unsafe void ProcessLimiter(CommandList context, ref LimiterState state) { - Debug.Assert(Parameter.IsChannelCountValid()); + Debug.Assert(_parameter.IsChannelCountValid()); - if (IsEffectEnabled && Parameter.IsChannelCountValid()) + if (IsEffectEnabled && _parameter.IsChannelCountValid()) { - if (!ResultState.IsEmpty && Parameter.StatisticsReset) + if (!ResultState.IsEmpty && _parameter.StatisticsReset) { ref LimiterStatistics statistics = ref MemoryMarshal.Cast(ResultState.Span[0].SpecificData)[0]; statistics.Reset(); } - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; + Span inputBuffers = stackalloc IntPtr[_parameter.ChannelCount]; + Span outputBuffers = stackalloc IntPtr[_parameter.ChannelCount]; - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]); outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]); } - for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++) + for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) { for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++) { float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex); - float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain; + float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain; float sampleInputMax = Math.Abs(inputSample); - float inputCoefficient = Parameter.ReleaseCoefficient; + float inputCoefficient = _parameter.ReleaseCoefficient; if (sampleInputMax > state.DetectorAverage[channelIndex].Read()) { - inputCoefficient = Parameter.AttackCoefficient; + inputCoefficient = _parameter.AttackCoefficient; } float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient); float attenuation = 1.0f; - if (detectorValue > Parameter.Threshold) + if (detectorValue > _parameter.Threshold) { - attenuation = Parameter.Threshold / detectorValue; + attenuation = _parameter.Threshold / detectorValue; } - float outputCoefficient = Parameter.ReleaseCoefficient; + float outputCoefficient = _parameter.ReleaseCoefficient; if (state.CompressionGainAverage[channelIndex].Read() > attenuation) { - outputCoefficient = Parameter.AttackCoefficient; + outputCoefficient = _parameter.AttackCoefficient; } float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient); - ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; + ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; - float outputSample = delayedSample * compressionGain * Parameter.OutputGain; + float outputSample = delayedSample * compressionGain * _parameter.OutputGain; *((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue; @@ -141,9 +141,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command state.DelayedSampleBufferPosition[channelIndex]++; - while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin) + while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin) { - state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin; + state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin; } if (!ResultState.IsEmpty) @@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } else { - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { if (InputBufferIndices[i] != OutputBufferIndices[i]) { diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs index b403f13703..c00118e49a 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs @@ -90,9 +90,16 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect public bool MakeupGainEnabled; /// - /// Reserved/padding. + /// Indicate if the compressor effect should output statistics. /// - private Array2 _reserved; + [MarshalAs(UnmanagedType.I1)] + public bool StatisticsEnabled; + + /// + /// Indicate to the DSP that the user did a statistics reset. + /// + [MarshalAs(UnmanagedType.I1)] + public bool StatisticsReset; /// /// Check if the is valid. diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorStatistics.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorStatistics.cs new file mode 100644 index 0000000000..65335e2d99 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorStatistics.cs @@ -0,0 +1,38 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Parameter.Effect +{ + /// + /// Effect result state for . + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct CompressorStatistics + { + /// + /// Maximum input mean value since last reset. + /// + public float MaximumMean; + + /// + /// Minimum output gain since last reset. + /// + public float MinimumGain; + + /// + /// Last processed input sample, per channel. + /// + public Array6 LastSamples; + + /// + /// Reset the statistics. + /// + /// Number of channels to reset. + public void Reset(ushort channelCount) + { + MaximumMean = 0.0f; + MinimumGain = 1.0f; + LastSamples.AsSpan()[..channelCount].Clear(); + } + } +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs index 807232f208..7ee49f11a6 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs @@ -28,6 +28,11 @@ namespace Ryujinx.Audio.Renderer.Parameter /// bool IsUsed { get; } + /// + /// Set to true to force resetting the previous mix volumes. + /// + bool ResetPrevVolume { get; } + /// /// Mix buffer volumes. /// diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs index 029c001ea9..f346efcb05 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs @@ -37,10 +37,16 @@ namespace Ryujinx.Audio.Renderer.Parameter [MarshalAs(UnmanagedType.I1)] public bool IsUsed; + /// + /// Set to true to force resetting the previous mix volumes. + /// + [MarshalAs(UnmanagedType.I1)] + public bool ResetPrevVolume; + /// /// Reserved/padding. /// - private unsafe fixed byte _reserved[3]; + private unsafe fixed byte _reserved[2]; [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] private struct MixArray { } @@ -58,6 +64,7 @@ namespace Ryujinx.Audio.Renderer.Parameter readonly Array2 ISplitterDestinationInParameter.BiquadFilters => default; readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; + readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; /// /// The expected constant of any input header. diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs index 312be8b707..1d867919d4 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs @@ -42,10 +42,16 @@ namespace Ryujinx.Audio.Renderer.Parameter [MarshalAs(UnmanagedType.I1)] public bool IsUsed; + /// + /// Set to true to force resetting the previous mix volumes. + /// + [MarshalAs(UnmanagedType.I1)] + public bool ResetPrevVolume; + /// /// Reserved/padding. /// - private unsafe fixed byte _reserved[11]; + private unsafe fixed byte _reserved[10]; [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] private struct MixArray { } @@ -63,6 +69,7 @@ namespace Ryujinx.Audio.Renderer.Parameter readonly Array2 ISplitterDestinationInParameter.BiquadFilters => BiquadFilters; readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; + readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; /// /// The expected constant of any input header. diff --git a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs index 32c7de6cfb..f725eb9f3e 100644 --- a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs @@ -108,10 +108,18 @@ namespace Ryujinx.Audio.Renderer.Server /// This was added in system update 17.0.0 public const int Revision12 = 12 << 24; + /// + /// REV13: + /// The compressor effect can now output statistics. + /// Splitter destinations now explicitly reset the previous mix volume, instead of doing so on first use. + /// + /// This was added in system update 18.0.0 + public const int Revision13 = 13 << 24; + /// /// Last revision supported by the implementation. /// - public const int LastRevision = Revision12; + public const int LastRevision = Revision13; /// /// Target revision magic supported by the implementation. @@ -384,6 +392,15 @@ namespace Ryujinx.Audio.Renderer.Server return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12); } + /// + /// Check if the audio renderer should support explicit previous mix volume reset on splitter. + /// + /// True if the audio renderer support explicit previous mix volume reset on splitter + public bool IsSplitterPrevVolumeResetSupported() + { + return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision13); + } + /// /// Get the version of the . /// diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs index 702f05462f..4c353b37e1 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs @@ -583,11 +583,20 @@ namespace Ryujinx.Audio.Renderer.Server } } - public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory state, bool isEnabled, int nodeId) + /// + /// Generate a new . + /// + /// The target buffer offset. + /// The compressor parameter. + /// The compressor state. + /// The DSP effect result state. + /// Set to true if the effect should be active. + /// The node id associated to this command. + public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory state, Memory effectResultState, bool isEnabled, int nodeId) { if (parameter.IsChannelCountValid()) { - CompressorCommand command = new(bufferOffset, parameter, state, isEnabled, nodeId); + CompressorCommand command = new(bufferOffset, parameter, state, effectResultState, isEnabled, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs index d798230c1d..0b789537a7 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs @@ -735,14 +735,26 @@ namespace Ryujinx.Audio.Renderer.Server } } - private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId) + private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId, int effectId) { Debug.Assert(effect.Type == EffectType.Compressor); + Memory dspResultState; + + if (effect.Parameter.StatisticsEnabled) + { + dspResultState = _effectContext.GetDspStateMemory(effectId); + } + else + { + dspResultState = Memory.Empty; + } + _commandBuffer.GenerateCompressorEffect( bufferOffset, effect.Parameter, effect.State, + dspResultState, effect.IsEnabled, nodeId); } @@ -795,7 +807,7 @@ namespace Ryujinx.Audio.Renderer.Server GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId); break; case EffectType.Compressor: - GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId); + GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId, effectId); break; default: throw new NotImplementedException($"Unsupported effect type {effect.Type}"); diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs index 06f135a886..bc9ba073d6 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs @@ -169,14 +169,28 @@ namespace Ryujinx.Audio.Renderer.Server { if (command.Enabled) { - return command.Parameter.ChannelCount switch + if (command.Parameter.StatisticsEnabled) { - 1 => 34431, - 2 => 44253, - 4 => 63827, - 6 => 83361, - _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), - }; + return command.Parameter.ChannelCount switch + { + 1 => 22100, + 2 => 33211, + 4 => 41587, + 6 => 58819, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; + } + else + { + return command.Parameter.ChannelCount switch + { + 1 => 19052, + 2 => 29852, + 4 => 37904, + 6 => 55020, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; + } } return command.Parameter.ChannelCount switch @@ -191,14 +205,28 @@ namespace Ryujinx.Audio.Renderer.Server if (command.Enabled) { - return command.Parameter.ChannelCount switch + if (command.Parameter.StatisticsEnabled) { - 1 => 51095, - 2 => 65693, - 4 => 95383, - 6 => 124510, - _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), - }; + return command.Parameter.ChannelCount switch + { + 1 => 32518, + 2 => 49102, + 4 => 61685, + 6 => 87250, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; + } + else + { + return command.Parameter.ChannelCount switch + { + 1 => 27963, + 2 => 44016, + 4 => 56183, + 6 => 81862, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; + } } return command.Parameter.ChannelCount switch diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs index eff60e7da8..de0f44e475 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs @@ -62,6 +62,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect UpdateUsageStateForCommandGeneration(); Parameter.Status = UsageState.Enabled; + Parameter.StatisticsReset = false; + } + + public override void InitializeResultState(ref EffectResultState state) + { + ref CompressorStatistics statistics = ref MemoryMarshal.Cast(state.SpecificData)[0]; + + statistics.Reset(Parameter.ChannelCount); + } + + public override void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState) + { + destState = srcState; } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs index a7b82a6bdc..6dddb43158 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs @@ -51,6 +51,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// public bool IsBugFixed { get; private set; } + /// + /// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use. + /// + public bool IsSplitterPrevVolumeResetSupported { get; private set; } + /// /// Initialize . /// @@ -139,6 +144,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } } + IsSplitterPrevVolumeResetSupported = behaviourContext.IsSplitterPrevVolumeResetSupported(); + SplitterState.InitializeSplitters(splitters.Span); Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed()); @@ -277,7 +284,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { SplitterDestination destination = GetDestination(parameter.Id); - destination.Update(parameter); + destination.Update(parameter, IsSplitterPrevVolumeResetSupported); } return true; diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs index 36dfa5e413..1a46d41fde 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs @@ -184,15 +184,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Update the splitter destination data from user parameter. /// /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter + /// Indicates that the audio renderer revision in use supports explicitly resetting the volume. + public void Update(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter { if (Unsafe.IsNullRef(ref _v2)) { - _v1.Update(parameter); + _v1.Update(parameter, isPrevVolumeResetSupported); } else { - _v2.Update(parameter); + _v2.Update(parameter, isPrevVolumeResetSupported); } } diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs index 5d2b8fb0fe..ce8f33685f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs @@ -93,7 +93,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Update the from user parameter. /// /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter + /// Indicates that the audio renderer revision in use supports explicitly resetting the volume. + public void Update(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter { Debug.Assert(Id == parameter.Id); @@ -103,7 +104,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter parameter.MixBufferVolume.CopyTo(MixBufferVolume); - if (!IsUsed && parameter.IsUsed) + bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed; + if (resetPrevVolume) { MixBufferVolume.CopyTo(PreviousMixBufferVolume); diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs index f9487909d4..5f96ef3aa5 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs @@ -98,7 +98,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Update the from user parameter. /// /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter + /// Indicates that the audio renderer revision in use supports explicitly resetting the volume. + public void Update(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter { Debug.Assert(Id == parameter.Id); @@ -110,7 +111,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter _biquadFilters = parameter.BiquadFilters; - if (!IsUsed && parameter.IsUsed) + bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed; + if (resetPrevVolume) { MixBufferVolume.CopyTo(PreviousMixBufferVolume); diff --git a/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs b/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs index c18bfee9fc..5914a747cb 100644 --- a/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs +++ b/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs @@ -8,5 +8,6 @@ namespace Ryujinx.Horizon.Sdk.Audio public static Result DeviceNotFound => new(ModuleId, 1); public static Result UnsupportedRevision => new(ModuleId, 2); + public static Result NotImplemented => new(ModuleId, 513); } } diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs index f67ea72988..2d3aa7ba92 100644 --- a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs @@ -233,6 +233,48 @@ namespace Ryujinx.Horizon.Sdk.Audio.Detail return Result.Success; } + [CmifCommand(15)] // 17.0.0+ + public Result AcquireAudioOutputDeviceNotification([CopyHandle] out int eventHandle, ulong deviceId) + { + eventHandle = 0; + + return AudioResult.NotImplemented; + } + + [CmifCommand(16)] // 17.0.0+ + public Result ReleaseAudioOutputDeviceNotification(ulong deviceId) + { + return AudioResult.NotImplemented; + } + + [CmifCommand(17)] // 17.0.0+ + public Result AcquireAudioInputDeviceNotification([CopyHandle] out int eventHandle, ulong deviceId) + { + eventHandle = 0; + + return AudioResult.NotImplemented; + } + + [CmifCommand(18)] // 17.0.0+ + public Result ReleaseAudioInputDeviceNotification(ulong deviceId) + { + return AudioResult.NotImplemented; + } + + [CmifCommand(19)] // 18.0.0+ + public Result SetAudioDeviceOutputVolumeAutoTuneEnabled(bool enabled) + { + return AudioResult.NotImplemented; + } + + [CmifCommand(20)] // 18.0.0+ + public Result IsAudioDeviceOutputVolumeAutoTuneEnabled(out bool enabled) + { + enabled = false; + + return AudioResult.NotImplemented; + } + protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs index 3e48a5b4ec..0b0ed7a542 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs @@ -55,6 +55,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -83,6 +84,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -111,6 +113,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -139,6 +142,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.75f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -167,6 +171,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -195,6 +200,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -223,6 +229,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -251,6 +258,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -279,6 +287,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -307,6 +316,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(4, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -335,6 +345,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -363,6 +374,36 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision13() + { + BehaviourContext behaviourContext = new(); + + behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision13); + + Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourContext.IsSplitterSupported()); + Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); + Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); + Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); + Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); + Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); + Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsTrue(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());