From cbad43b003fd8c0c01c54d7a91e0697c7ec4efab Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 20:35:49 -0600 Subject: [PATCH 001/368] some initial metal commits cherry-picked --- Directory.Packages.props | 1 + Ryujinx.sln | 8 + .../Configuration/GraphicsBackend.cs | 1 + src/Ryujinx.Graphics.Metal/Constants.cs | 13 + src/Ryujinx.Graphics.Metal/CounterEvent.cs | 23 + src/Ryujinx.Graphics.Metal/EnumConversion.cs | 206 +++ .../FormatCapabilities.cs | 14 + src/Ryujinx.Graphics.Metal/FormatTable.cs | 172 +++ src/Ryujinx.Graphics.Metal/HardwareInfo.cs | 82 ++ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 247 ++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 402 ++++++ .../Ryujinx.Graphics.Metal.csproj | 17 + src/Ryujinx.Graphics.Metal/Sampler.cs | 19 + src/Ryujinx.Graphics.Metal/Texture.cs | 151 ++ src/Ryujinx.Graphics.Metal/Window.cs | 55 + .../CodeGen/Msl/CodeGenContext.cs | 88 ++ .../CodeGen/Msl/Declarations.cs | 16 + .../CodeGen/Msl/Instructions/IoMap.cs | 29 + .../CodeGen/Msl/MslGenerator.cs | 17 + .../Translation/TargetApi.cs | 1 + .../Translation/TargetLanguage.cs | 2 +- src/Ryujinx/AppHost.cs | 2 + src/Ryujinx/AppHost.cs.orig | 1225 +++++++++++++++++ src/Ryujinx/Program.cs | 1 + .../UI/Renderer/EmbeddedWindowMetal.cs | 25 + .../UI/ViewModels/SettingsViewModel.cs | 2 + .../Views/Settings/SettingsGraphicsView.axaml | 3 + 27 files changed, 2821 insertions(+), 1 deletion(-) create mode 100644 src/Ryujinx.Graphics.Metal/Constants.cs create mode 100644 src/Ryujinx.Graphics.Metal/CounterEvent.cs create mode 100644 src/Ryujinx.Graphics.Metal/EnumConversion.cs create mode 100644 src/Ryujinx.Graphics.Metal/FormatCapabilities.cs create mode 100644 src/Ryujinx.Graphics.Metal/FormatTable.cs create mode 100644 src/Ryujinx.Graphics.Metal/HardwareInfo.cs create mode 100644 src/Ryujinx.Graphics.Metal/MetalRenderer.cs create mode 100644 src/Ryujinx.Graphics.Metal/Pipeline.cs create mode 100644 src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj create mode 100644 src/Ryujinx.Graphics.Metal/Sampler.cs create mode 100644 src/Ryujinx.Graphics.Metal/Texture.cs create mode 100644 src/Ryujinx.Graphics.Metal/Window.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs create mode 100644 src/Ryujinx/AppHost.cs.orig create mode 100644 src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 34655164e..73659e1d7 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,6 +44,7 @@ + diff --git a/Ryujinx.sln b/Ryujinx.sln index c3cb5a2b0..71d5f6dd9 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -80,6 +80,10 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}" + ProjectSection(ProjectDependencies) = postProject + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" ProjectSection(SolutionItems) = preProject @@ -257,6 +261,10 @@ Global {4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs index e3b4f91b0..ba68fefbe 100644 --- a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs +++ b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -8,5 +8,6 @@ namespace Ryujinx.Common.Configuration { Vulkan, OpenGl, + Metal } } diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs new file mode 100644 index 000000000..06fd84a52 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Metal +{ + static class Constants + { + // TODO: Check these values, these were largely copied from Vulkan + public const int MaxShaderStages = 5; + public const int MaxUniformBuffersPerStage = 18; + public const int MaxStorageBuffersPerStage = 16; + public const int MaxTexturesPerStage = 64; + public const int MaxCommandBuffersPerQueue = 16; + public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/CounterEvent.cs b/src/Ryujinx.Graphics.Metal/CounterEvent.cs new file mode 100644 index 000000000..eec810991 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/CounterEvent.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Metal +{ + public class CounterEvent : ICounterEvent + { + + public CounterEvent() + { + Invalid = false; + } + + public bool Invalid { get; set; } + public bool ReserveForHostAccess() + { + return true; + } + + public void Flush() { } + + public void Dispose() { } + } +} diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs new file mode 100644 index 000000000..0e23d8804 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -0,0 +1,206 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal; + +namespace Ryujinx.Graphics.Metal +{ + static class EnumConversion + { + public static MTLSamplerAddressMode Convert(this AddressMode mode) + { + return mode switch + { + AddressMode.Clamp => MTLSamplerAddressMode.ClampToEdge, // TODO: Should be clamp. + AddressMode.Repeat => MTLSamplerAddressMode.Repeat, + AddressMode.MirrorClamp => MTLSamplerAddressMode.MirrorClampToEdge, // TODO: Should be mirror clamp. + AddressMode.MirroredRepeat => MTLSamplerAddressMode.MirrorRepeat, + AddressMode.ClampToBorder => MTLSamplerAddressMode.ClampToBorderColor, + AddressMode.ClampToEdge => MTLSamplerAddressMode.ClampToEdge, + AddressMode.MirrorClampToEdge => MTLSamplerAddressMode.MirrorClampToEdge, + AddressMode.MirrorClampToBorder => MTLSamplerAddressMode.ClampToBorderColor, // TODO: Should be mirror clamp to border. + _ => LogInvalidAndReturn(mode, nameof(AddressMode), MTLSamplerAddressMode.ClampToEdge) // TODO: Should be clamp. + }; + } + + public static MTLBlendFactor Convert(this BlendFactor factor) + { + return factor switch + { + BlendFactor.Zero or BlendFactor.ZeroGl => MTLBlendFactor.Zero, + BlendFactor.One or BlendFactor.OneGl => MTLBlendFactor.One, + BlendFactor.SrcColor or BlendFactor.SrcColorGl => MTLBlendFactor.SourceColor, + BlendFactor.OneMinusSrcColor or BlendFactor.OneMinusSrcColorGl => MTLBlendFactor.OneMinusSourceColor, + BlendFactor.SrcAlpha or BlendFactor.SrcAlphaGl => MTLBlendFactor.SourceAlpha, + BlendFactor.OneMinusSrcAlpha or BlendFactor.OneMinusSrcAlphaGl => MTLBlendFactor.OneMinusSourceAlpha, + BlendFactor.DstAlpha or BlendFactor.DstAlphaGl => MTLBlendFactor.DestinationAlpha, + BlendFactor.OneMinusDstAlpha or BlendFactor.OneMinusDstAlphaGl => MTLBlendFactor.OneMinusDestinationAlpha, + BlendFactor.DstColor or BlendFactor.DstColorGl => MTLBlendFactor.DestinationColor, + BlendFactor.OneMinusDstColor or BlendFactor.OneMinusDstColorGl => MTLBlendFactor.OneMinusDestinationColor, + BlendFactor.SrcAlphaSaturate or BlendFactor.SrcAlphaSaturateGl => MTLBlendFactor.SourceAlphaSaturated, + BlendFactor.Src1Color or BlendFactor.Src1ColorGl => MTLBlendFactor.Source1Color, + BlendFactor.OneMinusSrc1Color or BlendFactor.OneMinusSrc1ColorGl => MTLBlendFactor.OneMinusSource1Color, + BlendFactor.Src1Alpha or BlendFactor.Src1AlphaGl => MTLBlendFactor.Source1Alpha, + BlendFactor.OneMinusSrc1Alpha or BlendFactor.OneMinusSrc1AlphaGl => MTLBlendFactor.OneMinusSource1Alpha, + BlendFactor.ConstantColor => MTLBlendFactor.BlendColor, + BlendFactor.OneMinusConstantColor => MTLBlendFactor.OneMinusBlendColor, + BlendFactor.ConstantAlpha => MTLBlendFactor.BlendAlpha, + BlendFactor.OneMinusConstantAlpha => MTLBlendFactor.OneMinusBlendAlpha, + _ => LogInvalidAndReturn(factor, nameof(BlendFactor), MTLBlendFactor.Zero) + }; + } + + public static MTLBlendOperation Convert(this BlendOp op) + { + return op switch + { + BlendOp.Add or BlendOp.AddGl => MTLBlendOperation.Add, + BlendOp.Subtract or BlendOp.SubtractGl => MTLBlendOperation.Subtract, + BlendOp.ReverseSubtract or BlendOp.ReverseSubtractGl => MTLBlendOperation.ReverseSubtract, + BlendOp.Minimum => MTLBlendOperation.Min, + BlendOp.Maximum => MTLBlendOperation.Max, + _ => LogInvalidAndReturn(op, nameof(BlendOp), MTLBlendOperation.Add) + }; + } + + public static MTLCompareFunction Convert(this CompareOp op) + { + return op switch + { + CompareOp.Never or CompareOp.NeverGl => MTLCompareFunction.Never, + CompareOp.Less or CompareOp.LessGl => MTLCompareFunction.Less, + CompareOp.Equal or CompareOp.EqualGl => MTLCompareFunction.Equal, + CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => MTLCompareFunction.LessEqual, + CompareOp.Greater or CompareOp.GreaterGl => MTLCompareFunction.Greater, + CompareOp.NotEqual or CompareOp.NotEqualGl => MTLCompareFunction.NotEqual, + CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => MTLCompareFunction.GreaterEqual, + CompareOp.Always or CompareOp.AlwaysGl => MTLCompareFunction.Always, + _ => LogInvalidAndReturn(op, nameof(CompareOp), MTLCompareFunction.Never) + }; + } + + public static MTLCullMode Convert(this Face face) + { + return face switch + { + Face.Back => MTLCullMode.Back, + Face.Front => MTLCullMode.Front, + Face.FrontAndBack => MTLCullMode.None, + _ => LogInvalidAndReturn(face, nameof(Face), MTLCullMode.Back) + }; + } + + public static MTLWinding Convert(this FrontFace frontFace) + { + return frontFace switch + { + FrontFace.Clockwise => MTLWinding.Clockwise, + FrontFace.CounterClockwise => MTLWinding.CounterClockwise, + _ => LogInvalidAndReturn(frontFace, nameof(FrontFace), MTLWinding.Clockwise) + }; + } + + public static MTLIndexType Convert(this IndexType type) + { + return type switch + { + IndexType.UShort => MTLIndexType.UInt16, + IndexType.UInt => MTLIndexType.UInt32, + _ => LogInvalidAndReturn(type, nameof(IndexType), MTLIndexType.UInt16) + }; + } + + public static MTLSamplerMinMagFilter Convert(this MagFilter filter) + { + return filter switch + { + MagFilter.Nearest => MTLSamplerMinMagFilter.Nearest, + MagFilter.Linear => MTLSamplerMinMagFilter.Linear, + _ => LogInvalidAndReturn(filter, nameof(MagFilter), MTLSamplerMinMagFilter.Nearest) + }; + } + + public static (MTLSamplerMinMagFilter, MTLSamplerMipFilter) Convert(this MinFilter filter) + { + return filter switch + { + MinFilter.Nearest => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest), + MinFilter.Linear => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Linear), + MinFilter.NearestMipmapNearest => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest), + MinFilter.LinearMipmapNearest => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Nearest), + MinFilter.NearestMipmapLinear => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Linear), + MinFilter.LinearMipmapLinear => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Linear), + _ => LogInvalidAndReturn(filter, nameof(MinFilter), (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest)) + + }; + } + + // TODO: Metal does not have native support for Triangle Fans but it is possible to emulate with TriangleStrip and moving around the indices + public static MTLPrimitiveType Convert(this PrimitiveTopology topology) + { + return topology switch + { + PrimitiveTopology.Points => MTLPrimitiveType.Point, + PrimitiveTopology.Lines => MTLPrimitiveType.Line, + PrimitiveTopology.LineStrip => MTLPrimitiveType.LineStrip, + PrimitiveTopology.Triangles => MTLPrimitiveType.Triangle, + PrimitiveTopology.TriangleStrip => MTLPrimitiveType.TriangleStrip, + _ => LogInvalidAndReturn(topology, nameof(PrimitiveTopology), MTLPrimitiveType.Triangle) + }; + } + + public static MTLStencilOperation Convert(this StencilOp op) + { + return op switch + { + StencilOp.Keep or StencilOp.KeepGl => MTLStencilOperation.Keep, + StencilOp.Zero or StencilOp.ZeroGl => MTLStencilOperation.Zero, + StencilOp.Replace or StencilOp.ReplaceGl => MTLStencilOperation.Replace, + StencilOp.IncrementAndClamp or StencilOp.IncrementAndClampGl => MTLStencilOperation.IncrementClamp, + StencilOp.DecrementAndClamp or StencilOp.DecrementAndClampGl => MTLStencilOperation.DecrementClamp, + StencilOp.Invert or StencilOp.InvertGl => MTLStencilOperation.Invert, + StencilOp.IncrementAndWrap or StencilOp.IncrementAndWrapGl => MTLStencilOperation.IncrementWrap, + StencilOp.DecrementAndWrap or StencilOp.DecrementAndWrapGl => MTLStencilOperation.DecrementWrap, + _ => LogInvalidAndReturn(op, nameof(StencilOp), MTLStencilOperation.Keep) + }; + } + + public static MTLTextureType Convert(this Target target) + { + return target switch + { + Target.TextureBuffer => MTLTextureType.TypeTextureBuffer, + Target.Texture1D => MTLTextureType.Type1D, + Target.Texture1DArray => MTLTextureType.Type1DArray, + Target.Texture2D => MTLTextureType.Type2D, + Target.Texture2DArray => MTLTextureType.Type2DArray, + Target.Texture2DMultisample => MTLTextureType.Type2DMultisample, + Target.Texture2DMultisampleArray => MTLTextureType.Type2DMultisampleArray, + Target.Texture3D => MTLTextureType.Type3D, + Target.Cubemap => MTLTextureType.TypeCube, + Target.CubemapArray => MTLTextureType.TypeCubeArray, + _ => LogInvalidAndReturn(target, nameof(Target), MTLTextureType.Type2D) + }; + } + + public static MTLTextureSwizzle Convert(this SwizzleComponent swizzleComponent) + { + return swizzleComponent switch + { + SwizzleComponent.Zero => MTLTextureSwizzle.Zero, + SwizzleComponent.One => MTLTextureSwizzle.One, + SwizzleComponent.Red => MTLTextureSwizzle.Red, + SwizzleComponent.Green => MTLTextureSwizzle.Green, + SwizzleComponent.Blue => MTLTextureSwizzle.Blue, + SwizzleComponent.Alpha => MTLTextureSwizzle.Alpha, + _ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), MTLTextureSwizzle.Zero), + }; + } + + private static T2 LogInvalidAndReturn(T1 value, string name, T2 defaultValue = default) + { + Logger.Debug?.Print(LogClass.Gpu, $"Invalid {name} enum value: {value}."); + + return defaultValue; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/FormatCapabilities.cs b/src/Ryujinx.Graphics.Metal/FormatCapabilities.cs new file mode 100644 index 000000000..6dc56c59b --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/FormatCapabilities.cs @@ -0,0 +1,14 @@ +using SharpMetal; + +namespace Ryujinx.Graphics.Metal +{ + static class FormatCapabilities + { + public static MTLPixelFormat ConvertToMTLFormat(GAL.Format srcFormat) + { + var format = FormatTable.GetFormat(srcFormat); + + return format; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs new file mode 100644 index 000000000..a21271e1c --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -0,0 +1,172 @@ +using Ryujinx.Graphics.GAL; +using System; +using SharpMetal; + +namespace Ryujinx.Graphics.Metal +{ + static class FormatTable + { + private static readonly MTLPixelFormat[] _table; + + static FormatTable() + { + _table = new MTLPixelFormat[Enum.GetNames(typeof(Format)).Length]; + + Add(Format.R8Unorm, MTLPixelFormat.R8Unorm); + Add(Format.R8Snorm, MTLPixelFormat.R8Snorm); + Add(Format.R8Uint, MTLPixelFormat.R8Uint); + Add(Format.R8Sint, MTLPixelFormat.R8Sint); + Add(Format.R16Float, MTLPixelFormat.R16Float); + Add(Format.R16Unorm, MTLPixelFormat.R16Unorm); + Add(Format.R16Snorm, MTLPixelFormat.R16Snorm); + Add(Format.R16Uint, MTLPixelFormat.R16Uint); + Add(Format.R16Sint, MTLPixelFormat.R16Sint); + Add(Format.R32Float, MTLPixelFormat.R32Float); + Add(Format.R32Uint, MTLPixelFormat.R32Uint); + Add(Format.R32Sint, MTLPixelFormat.R32Sint); + Add(Format.R8G8Unorm, MTLPixelFormat.RG8Unorm); + Add(Format.R8G8Snorm, MTLPixelFormat.RG8Snorm); + Add(Format.R8G8Uint, MTLPixelFormat.RG8Uint); + Add(Format.R8G8Sint, MTLPixelFormat.RG8Sint); + Add(Format.R16G16Float, MTLPixelFormat.RG16Float); + Add(Format.R16G16Unorm, MTLPixelFormat.RG16Unorm); + Add(Format.R16G16Snorm, MTLPixelFormat.RG16Snorm); + Add(Format.R16G16Uint, MTLPixelFormat.RG16Uint); + Add(Format.R16G16Sint, MTLPixelFormat.RG16Sint); + Add(Format.R32G32Float, MTLPixelFormat.RG32Float); + Add(Format.R32G32Uint, MTLPixelFormat.RG32Uint); + Add(Format.R32G32Sint, MTLPixelFormat.RG32Sint); + // Add(Format.R8G8B8Unorm, MTLPixelFormat.R8G8B8Unorm); + // Add(Format.R8G8B8Snorm, MTLPixelFormat.R8G8B8Snorm); + // Add(Format.R8G8B8Uint, MTLPixelFormat.R8G8B8Uint); + // Add(Format.R8G8B8Sint, MTLPixelFormat.R8G8B8Sint); + // Add(Format.R16G16B16Float, MTLPixelFormat.R16G16B16Float); + // Add(Format.R16G16B16Unorm, MTLPixelFormat.R16G16B16Unorm); + // Add(Format.R16G16B16Snorm, MTLPixelFormat.R16G16B16SNorm); + // Add(Format.R16G16B16Uint, MTLPixelFormat.R16G16B16Uint); + // Add(Format.R16G16B16Sint, MTLPixelFormat.R16G16B16Sint); + // Add(Format.R32G32B32Float, MTLPixelFormat.R32G32B32Sfloat); + // Add(Format.R32G32B32Uint, MTLPixelFormat.R32G32B32Uint); + // Add(Format.R32G32B32Sint, MTLPixelFormat.R32G32B32Sint); + Add(Format.R8G8B8A8Unorm, MTLPixelFormat.RGBA8Unorm); + Add(Format.R8G8B8A8Snorm, MTLPixelFormat.RGBA8Snorm); + Add(Format.R8G8B8A8Uint, MTLPixelFormat.RGBA8Uint); + Add(Format.R8G8B8A8Sint, MTLPixelFormat.RGBA8Sint); + Add(Format.R16G16B16A16Float, MTLPixelFormat.RGBA16Float); + Add(Format.R16G16B16A16Unorm, MTLPixelFormat.RGBA16Unorm); + Add(Format.R16G16B16A16Snorm, MTLPixelFormat.RGBA16Snorm); + Add(Format.R16G16B16A16Uint, MTLPixelFormat.RGBA16Uint); + Add(Format.R16G16B16A16Sint, MTLPixelFormat.RGBA16Sint); + Add(Format.R32G32B32A32Float, MTLPixelFormat.RGBA32Float); + Add(Format.R32G32B32A32Uint, MTLPixelFormat.RGBA32Uint); + Add(Format.R32G32B32A32Sint, MTLPixelFormat.RGBA32Sint); + Add(Format.S8Uint, MTLPixelFormat.Stencil8); + Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm); + // Add(Format.S8UintD24Unorm, MTLPixelFormat.S8UintD24Unorm); + Add(Format.D32Float, MTLPixelFormat.Depth32Float); + Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24Unorm_Stencil8); + Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32Float_Stencil8); + Add(Format.R8G8B8A8Srgb, MTLPixelFormat.RGBA8Unorm_sRGB); + // Add(Format.R4G4Unorm, MTLPixelFormat.R4G4Unorm); + // Add(Format.R4G4B4A4Unorm, MTLPixelFormat.R4G4B4A4Unorm); + // Add(Format.R5G5B5X1Unorm, MTLPixelFormat.R5G5B5X1Unorm); + // Add(Format.R5G5B5A1Unorm, MTLPixelFormat.R5G5B5A1Unorm); + Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm); + Add(Format.R10G10B10A2Unorm, MTLPixelFormat.RGB10A2Unorm); + Add(Format.R10G10B10A2Uint, MTLPixelFormat.RGB10A2Uint); + Add(Format.R11G11B10Float, MTLPixelFormat.RG11B10Float); + Add(Format.R9G9B9E5Float, MTLPixelFormat.RGB9E5Float); + Add(Format.Bc1RgbaUnorm, MTLPixelFormat.BC1_RGBA); + Add(Format.Bc2Unorm, MTLPixelFormat.BC2_RGBA); + Add(Format.Bc3Unorm, MTLPixelFormat.BC3_RGBA); + Add(Format.Bc1RgbaSrgb, MTLPixelFormat.BC1_RGBA_sRGB); + Add(Format.Bc2Srgb, MTLPixelFormat.BC2_RGBA_sRGB); + Add(Format.Bc3Srgb, MTLPixelFormat.BC3_RGBA_sRGB); + Add(Format.Bc4Unorm, MTLPixelFormat.BC4_RUnorm); + Add(Format.Bc4Snorm, MTLPixelFormat.BC4_RSnorm); + Add(Format.Bc5Unorm, MTLPixelFormat.BC5_RGUnorm); + Add(Format.Bc5Snorm, MTLPixelFormat.BC5_RGSnorm); + Add(Format.Bc7Unorm, MTLPixelFormat.BC7_RGBAUnorm); + Add(Format.Bc7Srgb, MTLPixelFormat.BC7_RGBAUnorm_sRGB); + Add(Format.Bc6HSfloat, MTLPixelFormat.BC6H_RGBFloat); + Add(Format.Bc6HUfloat, MTLPixelFormat.BC6H_RGBUfloat); + Add(Format.Etc2RgbUnorm, MTLPixelFormat.ETC2_RGB8); + Add(Format.Etc2RgbaUnorm, MTLPixelFormat.ETC2_RGB8A1); + // Add(Format.Etc2RgbPtaUnorm, MTLPixelFormat.Etc2RgbPtaUnorm); + Add(Format.Etc2RgbSrgb, MTLPixelFormat.ETC2_RGB8_sRGB); + Add(Format.Etc2RgbaSrgb, MTLPixelFormat.ETC2_RGB8A1_sRGB); + // Add(Format.Etc2RgbPtaSrgb, MTLPixelFormat.Etc2RgbPtaSrgb); + // Add(Format.R8Uscaled, MTLPixelFormat.R8Uscaled); + // Add(Format.R8Sscaled, MTLPixelFormat.R8Sscaled); + // Add(Format.R16Uscaled, MTLPixelFormat.R16Uscaled); + // Add(Format.R16Sscaled, MTLPixelFormat.R16Sscaled); + // Add(Format.R32Uscaled, MTLPixelFormat.R32Uscaled); + // Add(Format.R32Sscaled, MTLPixelFormat.R32Sscaled); + // Add(Format.R8G8Uscaled, MTLPixelFormat.R8G8Uscaled); + // Add(Format.R8G8Sscaled, MTLPixelFormat.R8G8Sscaled); + // Add(Format.R16G16Uscaled, MTLPixelFormat.R16G16Uscaled); + // Add(Format.R16G16Sscaled, MTLPixelFormat.R16G16Sscaled); + // Add(Format.R32G32Uscaled, MTLPixelFormat.R32G32Uscaled); + // Add(Format.R32G32Sscaled, MTLPixelFormat.R32G32Sscaled); + // Add(Format.R8G8B8Uscaled, MTLPixelFormat.R8G8B8Uscaled); + // Add(Format.R8G8B8Sscaled, MTLPixelFormat.R8G8B8Sscaled); + // Add(Format.R16G16B16Uscaled, MTLPixelFormat.R16G16B16Uscaled); + // Add(Format.R16G16B16Sscaled, MTLPixelFormat.R16G16B16Sscaled); + // Add(Format.R32G32B32Uscaled, MTLPixelFormat.R32G32B32Uscaled); + // Add(Format.R32G32B32Sscaled, MTLPixelFormat.R32G32B32Sscaled); + // Add(Format.R8G8B8A8Uscaled, MTLPixelFormat.R8G8B8A8Uscaled); + // Add(Format.R8G8B8A8Sscaled, MTLPixelFormat.R8G8B8A8Sscaled); + // Add(Format.R16G16B16A16Uscaled, MTLPixelFormat.R16G16B16A16Uscaled); + // Add(Format.R16G16B16A16Sscaled, MTLPixelFormat.R16G16B16A16Sscaled); + // Add(Format.R32G32B32A32Uscaled, MTLPixelFormat.R32G32B32A32Uscaled); + // Add(Format.R32G32B32A32Sscaled, MTLPixelFormat.R32G32B32A32Sscaled); + // Add(Format.R10G10B10A2Snorm, MTLPixelFormat.A2B10G10R10SNormPack32); + // Add(Format.R10G10B10A2Sint, MTLPixelFormat.A2B10G10R10SintPack32); + // Add(Format.R10G10B10A2Uscaled, MTLPixelFormat.A2B10G10R10UscaledPack32); + // Add(Format.R10G10B10A2Sscaled, MTLPixelFormat.A2B10G10R10SscaledPack32); + Add(Format.Astc4x4Unorm, MTLPixelFormat.ASTC_4x4_LDR); + Add(Format.Astc5x4Unorm, MTLPixelFormat.ASTC_5x4_LDR); + Add(Format.Astc5x5Unorm, MTLPixelFormat.ASTC_5x5_LDR); + Add(Format.Astc6x5Unorm, MTLPixelFormat.ASTC_6x5_LDR); + Add(Format.Astc6x6Unorm, MTLPixelFormat.ASTC_6x6_LDR); + Add(Format.Astc8x5Unorm, MTLPixelFormat.ASTC_8x5_LDR); + Add(Format.Astc8x6Unorm, MTLPixelFormat.ASTC_8x6_LDR); + Add(Format.Astc8x8Unorm, MTLPixelFormat.ASTC_8x8_LDR); + Add(Format.Astc10x5Unorm, MTLPixelFormat.ASTC_10x5_LDR); + Add(Format.Astc10x6Unorm, MTLPixelFormat.ASTC_10x6_LDR); + Add(Format.Astc10x8Unorm, MTLPixelFormat.ASTC_10x8_LDR); + Add(Format.Astc10x10Unorm, MTLPixelFormat.ASTC_10x10_LDR); + Add(Format.Astc12x10Unorm, MTLPixelFormat.ASTC_12x10_LDR); + Add(Format.Astc12x12Unorm, MTLPixelFormat.ASTC_12x12_LDR); + Add(Format.Astc4x4Srgb, MTLPixelFormat.ASTC_4x4_sRGB); + Add(Format.Astc5x4Srgb, MTLPixelFormat.ASTC_5x4_sRGB); + Add(Format.Astc5x5Srgb, MTLPixelFormat.ASTC_5x5_sRGB); + Add(Format.Astc6x5Srgb, MTLPixelFormat.ASTC_6x5_sRGB); + Add(Format.Astc6x6Srgb, MTLPixelFormat.ASTC_6x6_sRGB); + Add(Format.Astc8x5Srgb, MTLPixelFormat.ASTC_8x5_sRGB); + Add(Format.Astc8x6Srgb, MTLPixelFormat.ASTC_8x6_sRGB); + Add(Format.Astc8x8Srgb, MTLPixelFormat.ASTC_8x8_sRGB); + Add(Format.Astc10x5Srgb, MTLPixelFormat.ASTC_10x5_sRGB); + Add(Format.Astc10x6Srgb, MTLPixelFormat.ASTC_10x6_sRGB); + Add(Format.Astc10x8Srgb, MTLPixelFormat.ASTC_10x8_sRGB); + Add(Format.Astc10x10Srgb, MTLPixelFormat.ASTC_10x10_sRGB); + Add(Format.Astc12x10Srgb, MTLPixelFormat.ASTC_12x10_sRGB); + Add(Format.Astc12x12Srgb, MTLPixelFormat.ASTC_12x12_sRGB); + Add(Format.B5G6R5Unorm, MTLPixelFormat.B5G6R5Unorm); + Add(Format.B5G5R5A1Unorm, MTLPixelFormat.BGR5A1Unorm); + Add(Format.A1B5G5R5Unorm, MTLPixelFormat.A1BGR5Unorm); + Add(Format.B8G8R8A8Unorm, MTLPixelFormat.BGRA8Unorm); + Add(Format.B8G8R8A8Srgb, MTLPixelFormat.BGRA8Unorm_sRGB); + } + + private static void Add(Format format, MTLPixelFormat mtlFormat) + { + _table[(int)format] = mtlFormat; + } + + public static MTLPixelFormat GetFormat(Format format) + { + return _table[(int)format]; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs b/src/Ryujinx.Graphics.Metal/HardwareInfo.cs new file mode 100644 index 000000000..3ca7cdfca --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/HardwareInfo.cs @@ -0,0 +1,82 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Metal +{ + static partial class HardwareInfoTools + { + + private readonly static IntPtr kCFAllocatorDefault = IntPtr.Zero; + private readonly static UInt32 kCFStringEncodingASCII = 0x0600; + private const string IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"; + private const string CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; + + [LibraryImport(IOKit, StringMarshalling = StringMarshalling.Utf8)] + private static partial IntPtr IOServiceMatching(string name); + + [LibraryImport(IOKit)] + private static partial IntPtr IOServiceGetMatchingService(IntPtr mainPort, IntPtr matching); + + [LibraryImport(IOKit)] + private static partial IntPtr IORegistryEntryCreateCFProperty(IntPtr entry, IntPtr key, IntPtr allocator, UInt32 options); + + [LibraryImport(CoreFoundation, StringMarshalling = StringMarshalling.Utf8)] + private static partial IntPtr CFStringCreateWithCString(IntPtr allocator, string cString, UInt32 encoding); + + [LibraryImport(CoreFoundation)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool CFStringGetCString(IntPtr theString, IntPtr buffer, long bufferSizes, UInt32 encoding); + + [LibraryImport(CoreFoundation)] + public static partial IntPtr CFDataGetBytePtr(IntPtr theData); + + static string GetNameFromId(uint id) + { + return id switch + { + 0x1002 => "AMD", + 0x106B => "Apple", + 0x10DE => "NVIDIA", + 0x13B5 => "ARM", + 0x8086 => "Intel", + _ => $"0x{id:X}" + }; + } + + public static string GetVendor() + { + var serviceDict = IOServiceMatching("IOGPU"); + var service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict); + var cfString = CFStringCreateWithCString(kCFAllocatorDefault, "vendor-id", kCFStringEncodingASCII); + var cfProperty = IORegistryEntryCreateCFProperty(service, cfString, kCFAllocatorDefault, 0); + + byte[] buffer = new byte[4]; + var bufferPtr = CFDataGetBytePtr(cfProperty); + Marshal.Copy(bufferPtr, buffer, 0, buffer.Length); + + var vendorId = BitConverter.ToUInt32(buffer); + + return GetNameFromId(vendorId); + } + + public static string GetModel() + { + var serviceDict = IOServiceMatching("IOGPU"); + var service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict); + var cfString = CFStringCreateWithCString(kCFAllocatorDefault, "model", kCFStringEncodingASCII); + var cfProperty = IORegistryEntryCreateCFProperty(service, cfString, kCFAllocatorDefault, 0); + + char[] buffer = new char[64]; + IntPtr bufferPtr = Marshal.AllocHGlobal(buffer.Length); + + if (CFStringGetCString(cfProperty, bufferPtr, buffer.Length, kCFStringEncodingASCII)) + { + var model = Marshal.PtrToStringUTF8(bufferPtr); + Marshal.FreeHGlobal(bufferPtr); + return model; + } + + return ""; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs new file mode 100644 index 000000000..dc6b3d05a --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -0,0 +1,247 @@ +using Ryujinx.Common.Configuration; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader.Translation; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public sealed class MetalRenderer : IRenderer + { + private readonly MTLDevice _device; + private readonly Window _window; + private readonly Pipeline _pipeline; + private readonly MTLCommandQueue _queue; + + public event EventHandler ScreenCaptured; + public bool PreferThreading => true; + public IPipeline Pipeline => _pipeline; + public IWindow Window => _window; + + public MetalRenderer() + { + _device = MTLDevice.CreateSystemDefaultDevice(); + _queue = _device.NewCommandQueue(); + _pipeline = new Pipeline(_device, _queue); + _window = new Window(this); + } + + public void Initialize(GraphicsDebugLevel logLevel) + { + } + + public void BackgroundContextAction(Action action, bool alwaysBackground = false) + { + throw new NotImplementedException(); + } + + public BufferHandle CreateBuffer(int size, BufferHandle storageHint) + { + return CreateBuffer(size, BufferAccess.Default); + } + + public BufferHandle CreateBuffer(IntPtr pointer, int size) + { + var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); + var bufferPtr = buffer.NativePtr; + return Unsafe.As(ref bufferPtr); + } + + public BufferHandle CreateBuffer(int size, BufferAccess access) + { + var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); + + if (access == BufferAccess.FlushPersistent) + { + buffer.SetPurgeableState(MTLPurgeableState.NonVolatile); + } + + var bufferPtr = buffer.NativePtr; + return Unsafe.As(ref bufferPtr); + } + + public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) + { + var library = _device.NewDefaultLibrary(); + throw new NotImplementedException(); + } + + public ISampler CreateSampler(SamplerCreateInfo info) + { + (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); + + var sampler = _device.NewSamplerState(new MTLSamplerDescriptor + { + BorderColor = MTLSamplerBorderColor.TransparentBlack, + MinFilter = minFilter, + MagFilter = info.MagFilter.Convert(), + MipFilter = mipFilter, + CompareFunction = info.CompareOp.Convert(), + LodMinClamp = info.MinLod, + LodMaxClamp = info.MaxLod, + LodAverage = false, + MaxAnisotropy = (uint)info.MaxAnisotropy, + SAddressMode = info.AddressU.Convert(), + TAddressMode = info.AddressV.Convert(), + RAddressMode = info.AddressP.Convert() + }); + + return new Sampler(sampler); + } + + public ITexture CreateTexture(TextureCreateInfo info) + { + return new Texture(_device, _pipeline, info); + } + + public bool PrepareHostMapping(IntPtr address, ulong size) + { + // TODO: Metal Host Mapping + return false; + } + + public void CreateSync(ulong id, bool strict) + { + throw new NotImplementedException(); + } + + public void DeleteBuffer(BufferHandle buffer) + { + MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); + mtlBuffer.SetPurgeableState(MTLPurgeableState.Empty); + } + + public unsafe PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) + { + MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); + return new PinnedSpan(IntPtr.Add(mtlBuffer.Contents, offset).ToPointer(), size); + } + + public Capabilities GetCapabilities() + { + // TODO: Finalize these values + return new Capabilities( + api: TargetApi.Metal, + vendorName: HardwareInfoTools.GetVendor(), + hasFrontFacingBug: false, + hasVectorIndexingBug: true, + needsFragmentOutputSpecialization: true, + reduceShaderPrecision: true, + supportsAstcCompression: true, + supportsBc123Compression: true, + supportsBc45Compression: true, + supportsBc67Compression: true, + supportsEtc2Compression: true, + supports3DTextureCompression: true, + supportsBgraFormat: true, + supportsR4G4Format: false, + supportsR4G4B4A4Format: true, + supportsSnormBufferTextureFormat: true, + supports5BitComponentFormat: true, + supportsBlendEquationAdvanced: false, + supportsFragmentShaderInterlock: true, + supportsFragmentShaderOrderingIntel: false, + supportsGeometryShader: false, + supportsGeometryShaderPassthrough: false, + supportsTransformFeedback: false, + supportsImageLoadFormatted: false, + supportsLayerVertexTessellation: false, + supportsMismatchingViewFormat: true, + supportsCubemapView: true, + supportsNonConstantTextureOffset: false, + supportsShaderBallot: false, + supportsShaderBarrierDivergence: false, + supportsShaderFloat64: false, + supportsTextureShadowLod: false, + supportsViewportIndexVertexTessellation: false, + supportsViewportMask: false, + supportsViewportSwizzle: false, + supportsIndirectParameters: true, + supportsDepthClipControl: false, + maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, + maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, + maximumTexturesPerStage: Constants.MaxTexturesPerStage, + maximumImagesPerStage: Constants.MaxTextureBindings, + maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength, + maximumSupportedAnisotropy: 0, + storageBufferOffsetAlignment: 0, + gatherBiasPrecision: 0 + ); + } + + public ulong GetCurrentSync() + { + throw new NotImplementedException(); + } + + public HardwareInfo GetHardwareInfo() + { + return new HardwareInfo(HardwareInfoTools.GetVendor(), HardwareInfoTools.GetModel()); + } + + public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) + { + throw new NotImplementedException(); + } + + public unsafe void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) + { + MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); + var span = new Span(mtlBuffer.Contents.ToPointer(), (int)mtlBuffer.Length); + data.CopyTo(span.Slice(offset)); + mtlBuffer.DidModifyRange(new NSRange + { + location = (ulong)offset, + length = (ulong)data.Length + }); + } + + public void UpdateCounters() + { + // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc + } + + public void PreFrame() + { + + } + + public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, float divisor, bool hostReserved) + { + // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc + var counterEvent = new CounterEvent(); + resultHandler?.Invoke(counterEvent, type == CounterType.SamplesPassed ? (ulong)1 : 0); + return counterEvent; + } + + public void ResetCounter(CounterType type) + { + // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc + } + + public void WaitSync(ulong id) + { + throw new NotImplementedException(); + } + + public void SetInterruptAction(Action interruptAction) + { + // Not needed for now + } + + public void Screenshot() + { + // TODO: Screenshots + } + + public void Dispose() + { + _window.Dispose(); + _pipeline.Dispose(); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs new file mode 100644 index 000000000..dd1a5e071 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -0,0 +1,402 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class Pipeline : IPipeline, IDisposable + { + private readonly MTLDevice _device; + private readonly MTLCommandQueue _mtlCommandQueue; + + private MTLCommandBuffer _commandBuffer; + private MTLRenderCommandEncoder _renderCommandEncoder; + private MTLRenderPipelineState _renderPipelineState; + private MTLBlitCommandEncoder _blitCommandEncoder; + + public MTLRenderCommandEncoder RenderCommandEncoder => _renderCommandEncoder; + public MTLBlitCommandEncoder BlitCommandEncoder => _blitCommandEncoder; + + private PrimitiveTopology _topology; + + private MTLBuffer _indexBuffer; + private MTLIndexType _indexType; + private ulong _indexBufferOffset; + + public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) + { + _device = device; + _mtlCommandQueue = commandQueue; + + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + var error = new NSError(IntPtr.Zero); + _renderPipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + if (error != IntPtr.Zero) + { + // throw new Exception($"Failed to create render pipeline state! {StringHelp}"); + throw new Exception($"Failed to create render pipeline state!"); + } + + CreateCommandBuffer(); + } + + public void Present() + { + _renderCommandEncoder.EndEncoding(); + _blitCommandEncoder.EndEncoding(); + // TODO: Give command buffer a valid MTLDrawable + // _commandBuffer.PresentDrawable(); + _commandBuffer.Commit(); + + CreateCommandBuffer(); + } + + public void CreateCommandBuffer() + { + _commandBuffer = _mtlCommandQueue.CommandBuffer(); + + _renderCommandEncoder = _commandBuffer.RenderCommandEncoder(new MTLRenderPassDescriptor()); + _renderCommandEncoder.SetRenderPipelineState(_renderPipelineState); + + _blitCommandEncoder = _commandBuffer.BlitCommandEncoder(new MTLBlitPassDescriptor()); + } + + public void Barrier() + { + throw new NotImplementedException(); + } + + public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) + { + throw new NotImplementedException(); + } + + public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) + { + throw new NotImplementedException(); + } + + public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, + int stencilMask) + { + throw new NotImplementedException(); + } + + public void CommandBufferBarrier() + { + // TODO: Only required for MTLHeap or untracked resources + } + + public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) + { + throw new NotImplementedException(); + } + + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + { + throw new NotImplementedException(); + } + + public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + { + // TODO: Support topology re-indexing to provide support for TriangleFans + var _primitiveType = _topology.Convert(); + + _renderCommandEncoder.DrawPrimitives(_primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance); + } + + public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) + { + // TODO: Support topology re-indexing to provide support for TriangleFans + var _primitiveType = _topology.Convert(); + + _renderCommandEncoder.DrawIndexedPrimitives(_primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance); + } + + public void DrawIndexedIndirect(BufferRange indirectBuffer) + { + throw new NotImplementedException(); + } + + public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + throw new NotImplementedException(); + } + + public void DrawIndirect(BufferRange indirectBuffer) + { + throw new NotImplementedException(); + } + + public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + throw new NotImplementedException(); + } + + public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) + { + throw new NotImplementedException(); + } + + public void SetAlphaTest(bool enable, float reference, CompareOp op) + { + // Metal does not support alpha test. + } + + public void SetBlendState(AdvancedBlendDescriptor blend) + { + throw new NotImplementedException(); + } + + public void SetBlendState(int index, BlendDescriptor blend) + { + throw new NotImplementedException(); + } + + public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) + { + throw new NotImplementedException(); + } + + public void SetDepthClamp(bool clamp) + { + throw new NotImplementedException(); + } + + public void SetDepthMode(DepthMode mode) + { + throw new NotImplementedException(); + } + + public void SetDepthTest(DepthTestDescriptor depthTest) + { + throw new NotImplementedException(); + } + + public void SetFaceCulling(bool enable, Face face) + { + _renderCommandEncoder.SetCullMode(enable ? face.Convert() : MTLCullMode.None); + } + + public void SetFrontFace(FrontFace frontFace) + { + _renderCommandEncoder.SetFrontFacingWinding(frontFace.Convert()); + } + + public void SetIndexBuffer(BufferRange buffer, IndexType type) + { + if (buffer.Handle != BufferHandle.Null) + { + _indexType = type.Convert(); + _indexBufferOffset = (ulong)buffer.Offset; + var handle = buffer.Handle; + _indexBuffer = new(Unsafe.As(ref handle)); + } + } + + public void SetImage(int binding, ITexture texture, Format imageFormat) + { + throw new NotImplementedException(); + } + + public void SetLineParameters(float width, bool smooth) + { + // Not supported in Metal + } + + public void SetLogicOpState(bool enable, LogicalOp op) + { + // Not supported in Metal + } + + public void SetMultisampleState(MultisampleDescriptor multisample) + { + throw new NotImplementedException(); + } + + public void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel) + { + throw new NotImplementedException(); + } + + public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) + { + throw new NotImplementedException(); + } + + public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) + { + // Not supported in Metal + } + + public void SetPrimitiveRestart(bool enable, int index) + { + // TODO: Supported for LineStrip and TriangleStrip + // https://github.com/gpuweb/gpuweb/issues/1220#issuecomment-732483263 + // https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515520-drawindexedprimitives + // https://stackoverflow.com/questions/70813665/how-to-render-multiple-trianglestrips-using-metal + } + + public void SetPrimitiveTopology(PrimitiveTopology topology) + { + _topology = topology; + } + + public void SetProgram(IProgram program) + { + throw new NotImplementedException(); + } + + public void SetRasterizerDiscard(bool discard) + { + throw new NotImplementedException(); + } + + public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) + { + throw new NotImplementedException(); + } + + public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + { + throw new NotImplementedException(); + } + + public unsafe void SetScissors(ReadOnlySpan> regions) + { + // TODO: Test max allowed scissor rects on device + var mtlScissorRects = new MTLScissorRect[regions.Length]; + + for (int i = 0; i < regions.Length; i++) + { + var region = regions[i]; + mtlScissorRects[i] = new MTLScissorRect + { + height = (ulong)region.Height, + width = (ulong)region.Width, + x = (ulong)region.X, + y = (ulong)region.Y + }; + } + + fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) + { + // TODO: Fix this function which currently wont accept pointer as intended + // _renderCommandEncoder.SetScissorRects(pMtlScissorRects, regions.Length); + } + } + + public void SetStencilTest(StencilTestDescriptor stencilTest) + { + // TODO + } + + public void SetStorageBuffers(ReadOnlySpan buffers) + { + throw new NotImplementedException(); + } + + public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) + { + throw new NotImplementedException(); + } + + public void SetUniformBuffers(ReadOnlySpan buffers) + { + throw new NotImplementedException(); + } + + public void SetUserClipDistance(int index, bool enableClip) + { + throw new NotImplementedException(); + } + + public void SetVertexAttribs(ReadOnlySpan vertexAttribs) + { + throw new NotImplementedException(); + } + + public void SetVertexBuffers(ReadOnlySpan vertexBuffers) + { + throw new NotImplementedException(); + } + + public unsafe void SetViewports(ReadOnlySpan viewports) + { + // TODO: Test max allowed viewports on device + var mtlViewports = new MTLViewport[viewports.Length]; + + for (int i = 0; i < viewports.Length; i++) + { + var viewport = viewports[i]; + mtlViewports[i] = new MTLViewport + { + originX = viewport.Region.X, + originY = viewport.Region.Y, + width = viewport.Region.Width, + height = viewport.Region.Height, + znear = viewport.DepthNear, + zfar = viewport.DepthFar + }; + } + + fixed (MTLViewport* pMtlViewports = mtlViewports) + { + // TODO: Fix this function which currently wont accept pointer as intended + // _renderCommandEncoder.SetViewports(pMtlViewports, viewports.Length); + } + } + + public void TextureBarrier() + { + throw new NotImplementedException(); + } + + public void TextureBarrierTiled() + { + throw new NotImplementedException(); + } + + public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) + { + // TODO: Implementable via indirect draw commands + return false; + } + + public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) + { + // TODO: Implementable via indirect draw commands + return false; + } + + public void EndHostConditionalRendering() + { + // TODO: Implementable via indirect draw commands + } + + public void BeginTransformFeedback(PrimitiveTopology topology) + { + // Metal does not support Transform Feedback + } + + public void EndTransformFeedback() + { + // Metal does not support Transform Feedback + } + + public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) + { + // Metal does not support Transform Feedback + } + + public void Dispose() + { + + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj new file mode 100644 index 000000000..6e8b00183 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -0,0 +1,17 @@ + + + + net7.0 + true + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs new file mode 100644 index 000000000..a40040c5f --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -0,0 +1,19 @@ +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; + +namespace Ryujinx.Graphics.Metal +{ + public class Sampler : ISampler + { + private MTLSamplerState _mtlSamplerState; + + public Sampler(MTLSamplerState mtlSamplerState) + { + _mtlSamplerState = mtlSamplerState; + } + + public void Dispose() + { + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs new file mode 100644 index 000000000..b03fb26c8 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -0,0 +1,151 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + class Texture : ITexture, IDisposable + { + private readonly TextureCreateInfo _info; + private readonly Pipeline _pipeline; + private readonly MTLDevice _device; + + public MTLTexture MTLTexture; + public TextureCreateInfo Info => Info; + public int Width => Info.Width; + public int Height => Info.Height; + + public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) + { + _device = device; + _pipeline = pipeline; + _info = info; + + var descriptor = new MTLTextureDescriptor(); + descriptor.PixelFormat = FormatTable.GetFormat(Info.Format); + // descriptor.Usage = + descriptor.Width = (ulong)Width; + descriptor.Height = (ulong)Height; + descriptor.Depth = (ulong)Info.Depth; + descriptor.SampleCount = (ulong)Info.Samples; + descriptor.MipmapLevelCount = (ulong)Info.Levels; + descriptor.TextureType = Info.Target.Convert(); + descriptor.Swizzle = new MTLTextureSwizzleChannels + { + red = Info.SwizzleR.Convert(), + green = Info.SwizzleG.Convert(), + blue = Info.SwizzleB.Convert(), + alpha = Info.SwizzleA.Convert() + }; + + MTLTexture = _device.NewTexture(descriptor); + } + + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + if (destination is Texture destinationTexture) + { + _pipeline.BlitCommandEncoder.CopyFromTexture( + MTLTexture, + (ulong)firstLayer, + (ulong)firstLevel, + destinationTexture.MTLTexture, + (ulong)firstLayer, + (ulong)firstLevel, + MTLTexture.ArrayLength, + MTLTexture.MipmapLevelCount); + } + } + + public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + if (destination is Texture destinationTexture) + { + _pipeline.BlitCommandEncoder.CopyFromTexture( + MTLTexture, + (ulong)srcLayer, + (ulong)srcLevel, + destinationTexture.MTLTexture, + (ulong)dstLayer, + (ulong)dstLevel, + MTLTexture.ArrayLength, + MTLTexture.MipmapLevelCount); + } + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + var samplerDescriptor = new MTLSamplerDescriptor(); + samplerDescriptor.MinFilter = linearFilter ? MTLSamplerMinMagFilter.Linear : MTLSamplerMinMagFilter.Nearest; + samplerDescriptor.MagFilter = linearFilter ? MTLSamplerMinMagFilter.Linear : MTLSamplerMinMagFilter.Nearest; + var samplerState = _device.NewSamplerState(samplerDescriptor); + } + + public void CopyTo(BufferRange range, int layer, int level, int stride) + { + throw new NotImplementedException(); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + throw new NotImplementedException(); + } + + public PinnedSpan GetData() + { + throw new NotImplementedException(); + } + + public PinnedSpan GetData(int layer, int level) + { + throw new NotImplementedException(); + } + + public void SetData(SpanOrArray data) + { + throw new NotImplementedException(); + } + + public void SetData(SpanOrArray data, int layer, int level) + { + throw new NotImplementedException(); + } + + public unsafe void SetData(SpanOrArray data, int layer, int level, Rectangle region) + { + // TODO: Figure out bytesPerRow + // For an ordinary or packed pixel format, the stride, in bytes, between rows of source data. + // For a compressed pixel format, the stride is the number of bytes from the beginning of one row of blocks to the beginning of the next. + if (MTLTexture.IsSparse) + ulong bytesPerRow = 0; + var mtlRegion = new MTLRegion + { + origin = new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y }, + size = new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height }, + }; + + fixed (byte* pData = data.Span) + { + MTLTexture.ReplaceRegion(mtlRegion, (ulong)level, (ulong)layer, new IntPtr(pData), bytesPerRow, 0); + } + } + + public void SetStorage(BufferRange buffer) + { + throw new NotImplementedException(); + } + + public void Release() + { + throw new NotImplementedException(); + } + + public void Dispose() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs new file mode 100644 index 000000000..31c534958 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -0,0 +1,55 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class Window : IWindow, IDisposable + { + private readonly MetalRenderer _renderer; + + public Window(MetalRenderer renderer) + { + _renderer = renderer; + } + + public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + { + if (_renderer.Pipeline is Pipeline pipeline) + { + pipeline.Present(); + } + } + + public void SetSize(int width, int height) + { + // Not needed as we can get the size from the surface. + } + + public void ChangeVSyncMode(bool vsyncEnabled) + { + throw new NotImplementedException(); + } + + public void SetAntiAliasing(AntiAliasing antialiasing) + { + throw new NotImplementedException(); + } + + public void SetScalingFilter(ScalingFilter type) + { + throw new NotImplementedException(); + } + + public void SetScalingFilterLevel(float level) + { + throw new NotImplementedException(); + } + + public void Dispose() + { + + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs new file mode 100644 index 000000000..a84d99a04 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -0,0 +1,88 @@ +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; +using System.Text; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl +{ + class CodeGenContext + { + public const string Tab = " "; + + public StructuredProgramInfo Info { get; } + public ShaderConfig Config { get; } + + private readonly StringBuilder _sb; + + private int _level; + + private string _indentation; + + public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) + { + Info = info; + Config = Config; + + _sb = new StringBuilder(); + } + + public void AppendLine() + { + _sb.AppendLine(); + } + + public void AppendLine(string str) + { + _sb.AppendLine(_indentation + str); + } + + public string GetCode() + { + return _sb.ToString(); + } + + public void EnterScope() + { + AppendLine("{"); + + _level++; + + UpdateIndentation(); + } + + public void LeaveScope(string suffix = "") + { + if (_level == 0) + { + return; + } + + _level--; + + UpdateIndentation(); + + AppendLine("}" + suffix); + } + + public StructuredFunction GetFunction(int id) + { + return Info.Functions[id]; + } + + private void UpdateIndentation() + { + _indentation = GetIndentation(_level); + } + + private static string GetIndentation(int level) + { + string indentation = string.Empty; + + for (int index = 0; index < level; index++) + { + indentation += Tab; + } + + return indentation; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs new file mode 100644 index 000000000..47a8b477f --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.CodeGen.Glsl; +using Ryujinx.Graphics.Shader.StructuredIr; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl +{ + static class Declarations + { + public static void Declare(CodeGenContext context, StructuredProgramInfo info) + { + context.AppendLine("#include "); + context.AppendLine("#include "); + context.AppendLine(); + context.AppendLine("using namespace metal;"); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs new file mode 100644 index 000000000..261a0e572 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -0,0 +1,29 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + static class IoMap + { + public static (string, AggregateType) GetMSLBuiltIn(IoVariable ioVariable) + { + return ioVariable switch + { + IoVariable.BaseInstance => ("base_instance", AggregateType.S32), + IoVariable.BaseVertex => ("base_vertex", AggregateType.S32), + IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), + IoVariable.FragmentOutputColor => ("color", AggregateType.Vector2 | AggregateType.Vector3 | AggregateType.Vector4), + IoVariable.FragmentOutputDepth => ("depth", AggregateType.FP32), + IoVariable.FrontFacing => ("front_facing", AggregateType.Bool), + IoVariable.InstanceId => ("instance_id", AggregateType.S32), + IoVariable.PointCoord => ("point_coord", AggregateType.Vector2), + IoVariable.PointSize => ("point_size", AggregateType.FP32), + IoVariable.Position => ("position", AggregateType.Vector4), + IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), + IoVariable.VertexId => ("vertex_id", AggregateType.S32), + IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), + _ => (null, AggregateType.Invalid), + }; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs new file mode 100644 index 000000000..0dc82390f --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -0,0 +1,17 @@ +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl +{ + static class MslGenerator + { + public static string Generate(StructuredProgramInfo info, ShaderConfig config) + { + CodeGenContext context = new CodeGenContext(info, config); + + Declarations.Declare(context, info); + + return context.GetCode(); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs index 519600937..66ed3dd45 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs @@ -4,5 +4,6 @@ namespace Ryujinx.Graphics.Shader.Translation { OpenGL, Vulkan, + Metal } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs index 863c7447b..9d58cb926 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Graphics.Shader.Translation { Glsl, Spirv, - Arb, + Msl } } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 5872b278f..9cd44addb 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -30,6 +30,7 @@ using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; +using Ryujinx.Graphics.Metal; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; @@ -1137,6 +1138,7 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( vSyncMode, LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", + ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToText(), dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), $"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", diff --git a/src/Ryujinx/AppHost.cs.orig b/src/Ryujinx/AppHost.cs.orig new file mode 100644 index 000000000..99663fbc5 --- /dev/null +++ b/src/Ryujinx/AppHost.cs.orig @@ -0,0 +1,1225 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Input; +using Avalonia.Threading; +using LibHac.Tools.FsSystem; +using Ryujinx.Audio.Backends.Dummy; +using Ryujinx.Audio.Backends.OpenAL; +using Ryujinx.Audio.Backends.SDL2; +using Ryujinx.Audio.Backends.SoundIo; +using Ryujinx.Audio.Integration; +using Ryujinx.Ava.Common; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Input; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Renderer; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Multiplayer; +using Ryujinx.Common.Logging; +using Ryujinx.Common.SystemInterop; +using Ryujinx.Common.Utilities; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Multithreading; +using Ryujinx.Graphics.Gpu; +using Ryujinx.Graphics.OpenGL; +using Ryujinx.Graphics.Vulkan; +using Ryujinx.Graphics.Metal; +using Ryujinx.HLE; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.Input; +using Ryujinx.Input.HLE; +using Ryujinx.UI.App.Common; +using Ryujinx.UI.Common; +using Ryujinx.UI.Common.Configuration; +using Ryujinx.UI.Common.Helper; +using Silk.NET.Vulkan; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SPB.Graphics.Vulkan; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; +using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; +using Image = SixLabors.ImageSharp.Image; +using InputManager = Ryujinx.Input.HLE.InputManager; +using IRenderer = Ryujinx.Graphics.GAL.IRenderer; +using Key = Ryujinx.Input.Key; +using MouseButton = Ryujinx.Input.MouseButton; +using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; +using Size = Avalonia.Size; +using Switch = Ryujinx.HLE.Switch; + +namespace Ryujinx.Ava +{ + internal class AppHost + { + private const int CursorHideIdleTime = 5; // Hide Cursor seconds. + private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. + private const int TargetFps = 60; + private const float VolumeDelta = 0.05f; + + private static readonly Cursor _invisibleCursor = new(StandardCursorType.None); + private readonly IntPtr _invisibleCursorWin; + private readonly IntPtr _defaultCursorWin; + + private readonly long _ticksPerFrame; + private readonly Stopwatch _chrono; + private long _ticks; + + private readonly AccountManager _accountManager; + private readonly UserChannelPersistence _userChannelPersistence; + private readonly InputManager _inputManager; + + private readonly MainWindowViewModel _viewModel; + private readonly IKeyboard _keyboardInterface; + private readonly TopLevel _topLevel; + public RendererHost RendererHost; + + private readonly GraphicsDebugLevel _glLogLevel; + private float _newVolume; + private KeyboardHotkeyState _prevHotkeyState; + + private long _lastCursorMoveTime; + private bool _isCursorInRenderer = true; + + private bool _isStopped; + private bool _isActive; + private bool _renderingStarted; + + private readonly ManualResetEvent _gpuDoneEvent; + + private IRenderer _renderer; + private readonly Thread _renderingThread; + private readonly CancellationTokenSource _gpuCancellationTokenSource; + private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; + + private bool _dialogShown; + private readonly bool _isFirmwareTitle; + + private readonly object _lockObject = new(); + + public event EventHandler AppExit; + public event EventHandler StatusInitEvent; + public event EventHandler StatusUpdatedEvent; + + public VirtualFileSystem VirtualFileSystem { get; } + public ContentManager ContentManager { get; } + public NpadManager NpadManager { get; } + public TouchScreenManager TouchScreenManager { get; } + public Switch Device { get; set; } + + public int Width { get; private set; } + public int Height { get; private set; } + public string ApplicationPath { get; private set; } + public bool ScreenshotRequested { get; set; } + + public AppHost( + RendererHost renderer, + InputManager inputManager, + string applicationPath, + VirtualFileSystem virtualFileSystem, + ContentManager contentManager, + AccountManager accountManager, + UserChannelPersistence userChannelPersistence, + MainWindowViewModel viewmodel, + TopLevel topLevel) + { + _viewModel = viewmodel; + _inputManager = inputManager; + _accountManager = accountManager; + _userChannelPersistence = userChannelPersistence; + _renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" }; + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; + _topLevel = topLevel; + + _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer)); + + _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); + + NpadManager = _inputManager.CreateNpadManager(); + TouchScreenManager = _inputManager.CreateTouchScreenManager(); + ApplicationPath = applicationPath; + VirtualFileSystem = virtualFileSystem; + ContentManager = contentManager; + + RendererHost = renderer; + + _chrono = new Stopwatch(); + _ticksPerFrame = Stopwatch.Frequency / TargetFps; + + if (ApplicationPath.StartsWith("@SystemContent")) + { + ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); + + _isFirmwareTitle = true; + } + + ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; + + _topLevel.PointerMoved += TopLevel_PointerEnteredOrMoved; + _topLevel.PointerEntered += TopLevel_PointerEnteredOrMoved; + _topLevel.PointerExited += TopLevel_PointerExited; + + if (OperatingSystem.IsWindows()) + { + _invisibleCursorWin = CreateEmptyCursor(); + _defaultCursorWin = CreateArrowCursor(); + } + + ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; + ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; + ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; + ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough; + + ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState; + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; + ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState; + + _gpuCancellationTokenSource = new CancellationTokenSource(); + _gpuDoneEvent = new ManualResetEvent(false); + } + + private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e) + { + if (sender is MainWindow window) + { + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + + var point = e.GetCurrentPoint(window).Position; + var bounds = RendererHost.EmbeddedWindow.Bounds; + + _isCursorInRenderer = point.X >= bounds.X && + point.X <= bounds.Width + bounds.X && + point.Y >= bounds.Y && + point.Y <= bounds.Height + bounds.Y; + } + } + + private void TopLevel_PointerExited(object sender, PointerEventArgs e) + { + _isCursorInRenderer = false; + } + + private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs e) + { + _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + } + + private void UpdateScalingFilter(object sender, ReactiveEventArgs e) + { + _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + } + + private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs e) + { + _renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); + } + + private void ShowCursor() + { + Dispatcher.UIThread.Post(() => + { + _viewModel.Cursor = Cursor.Default; + + if (OperatingSystem.IsWindows()) + { + SetCursor(_defaultCursorWin); + } + }); + } + + private void HideCursor() + { + Dispatcher.UIThread.Post(() => + { + _viewModel.Cursor = _invisibleCursor; + + if (OperatingSystem.IsWindows()) + { + SetCursor(_invisibleCursorWin); + } + }); + } + + private void SetRendererWindowSize(Size size) + { + if (_renderer != null) + { + double scale = _topLevel.RenderScaling; + + _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); + } + } + + private void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e) + { + if (e.Data.Length > 0 && e.Height > 0 && e.Width > 0) + { + Task.Run(() => + { + lock (_lockObject) + { + string applicationName = Device.Processes.ActiveApplication.Name; + string sanitizedApplicationName = FileSystemUtils.SanitizeFileName(applicationName); + DateTime currentTime = DateTime.Now; + + string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; + + string directory = AppDataManager.Mode switch + { + AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), + _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), + }; + + string path = Path.Combine(directory, filename); + + try + { + Directory.CreateDirectory(directory); + } + catch (Exception ex) + { + Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {directory}. Error : {ex.GetType().Name}", "Screenshot"); + + return; + } + + Image image = e.IsBgra ? Image.LoadPixelData(e.Data, e.Width, e.Height) + : Image.LoadPixelData(e.Data, e.Width, e.Height); + + if (e.FlipX) + { + image.Mutate(x => x.Flip(FlipMode.Horizontal)); + } + + if (e.FlipY) + { + image.Mutate(x => x.Flip(FlipMode.Vertical)); + } + + image.SaveAsPng(path, new PngEncoder + { + ColorType = PngColorType.Rgb, + }); + + image.Dispose(); + + Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); + } + }); + } + else + { + Logger.Error?.Print(LogClass.Application, $"Screenshot is empty. Size : {e.Data.Length} bytes. Resolution : {e.Width}x{e.Height}", "Screenshot"); + } + } + + public void Start() + { + if (OperatingSystem.IsWindows()) + { + _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); + } + + DisplaySleep.Prevent(); + + NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); + TouchScreenManager.Initialize(Device); + + _viewModel.IsGameRunning = true; + + Dispatcher.UIThread.InvokeAsync(() => + { + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version); + }); + + _viewModel.SetUiProgressHandlers(Device); + + RendererHost.BoundsChanged += Window_BoundsChanged; + + _isActive = true; + + _renderingThread.Start(); + + _viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value; + + MainLoop(); + + Exit(); + } + + private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args) + { + if (Device != null) + { + Device.Configuration.IgnoreMissingServices = args.NewValue; + } + } + + private void UpdateAspectRatioState(object sender, ReactiveEventArgs args) + { + if (Device != null) + { + Device.Configuration.AspectRatio = args.NewValue; + } + } + + private void UpdateAntiAliasing(object sender, ReactiveEventArgs e) + { + _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); + } + + private void UpdateDockedModeState(object sender, ReactiveEventArgs e) + { + Device?.System.ChangeDockedModeState(e.NewValue); + } + + private void UpdateAudioVolumeState(object sender, ReactiveEventArgs e) + { + Device?.SetVolume(e.NewValue); + + Dispatcher.UIThread.Post(() => + { + _viewModel.Volume = e.NewValue; + }); + } + + private void UpdateEnableInternetAccessState(object sender, ReactiveEventArgs e) + { + Device.Configuration.EnableInternetAccess = e.NewValue; + } + + private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs e) + { + Device.Configuration.MultiplayerLanInterfaceId = e.NewValue; + } + + private void UpdateMultiplayerModeState(object sender, ReactiveEventArgs e) + { + Device.Configuration.MultiplayerMode = e.NewValue; + } + + public void ToggleVSync() + { + Device.EnableDeviceVsync = !Device.EnableDeviceVsync; + _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + } + + public void Stop() + { + _isActive = false; + } + + private void Exit() + { + (_keyboardInterface as AvaloniaKeyboard)?.Clear(); + + if (_isStopped) + { + return; + } + + _isStopped = true; + _isActive = false; + } + + public void DisposeContext() + { + Dispose(); + + _isActive = false; + + // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. + // We only need to wait for all commands submitted during the main gpu loop to be processed. + _gpuDoneEvent.WaitOne(); + _gpuDoneEvent.Dispose(); + + DisplaySleep.Restore(); + + NpadManager.Dispose(); + TouchScreenManager.Dispose(); + Device.Dispose(); + + DisposeGpu(); + + AppExit?.Invoke(this, EventArgs.Empty); + } + + private void Dispose() + { + if (Device.Processes != null) + { + MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); + } + + ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; + ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; + ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; + ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough; + + _topLevel.PointerMoved -= TopLevel_PointerEnteredOrMoved; + _topLevel.PointerEntered -= TopLevel_PointerEnteredOrMoved; + _topLevel.PointerExited -= TopLevel_PointerExited; + + _gpuCancellationTokenSource.Cancel(); + _gpuCancellationTokenSource.Dispose(); + + _chrono.Stop(); + } + + public void DisposeGpu() + { + if (OperatingSystem.IsWindows()) + { + _windowsMultimediaTimerResolution?.Dispose(); + _windowsMultimediaTimerResolution = null; + } + + if (RendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow) + { + // Try to bind the OpenGL context before calling the shutdown event. + openGlWindow.MakeCurrent(false, false); + + Device.DisposeGpu(); + + // Unbind context and destroy everything. + openGlWindow.MakeCurrent(true, false); + } + else + { + Device.DisposeGpu(); + } + } + + private void HideCursorState_Changed(object sender, ReactiveEventArgs state) + { + if (state.NewValue == HideCursorMode.OnIdle) + { + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + } + } + + public async Task LoadGuestApplication() + { + InitializeSwitchInstance(); + MainWindow.UpdateGraphicsConfig(); + + SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); + + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) + { + { + if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) + { + if (userError == UserError.NoFirmware) + { + UserResult result = await ContentDialogHelper.CreateConfirmationDialog( + LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + ""); + + if (result != UserResult.Yes) + { + await UserErrorDialog.ShowUserErrorDialog(userError); + Device.Dispose(); + + return false; + } + } + + if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) + { + await UserErrorDialog.ShowUserErrorDialog(userError); + Device.Dispose(); + + return false; + } + + // Tell the user that we installed a firmware for them. + if (userError == UserError.NoFirmware) + { + firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); + + _viewModel.RefreshFirmwareStatus(); + + await ContentDialogHelper.CreateInfoDialog( + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogOk], + "", + LocaleManager.Instance[LocaleKeys.RyujinxInfo]); + } + } + else + { + await UserErrorDialog.ShowUserErrorDialog(userError); + Device.Dispose(); + + return false; + } + } + } + } + + Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); + + if (_isFirmwareTitle) + { + Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); + + if (!Device.LoadNca(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + } + else if (Directory.Exists(ApplicationPath)) + { + string[] romFsFiles = Directory.GetFiles(ApplicationPath, "*.istorage"); + + if (romFsFiles.Length == 0) + { + romFsFiles = Directory.GetFiles(ApplicationPath, "*.romfs"); + } + + if (romFsFiles.Length > 0) + { + Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); + + if (!Device.LoadCart(ApplicationPath, romFsFiles[0])) + { + Device.Dispose(); + + return false; + } + } + else + { + Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); + + if (!Device.LoadCart(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + } + } + else if (File.Exists(ApplicationPath)) + { + switch (Path.GetExtension(ApplicationPath).ToLowerInvariant()) + { + case ".xci": + { + Logger.Info?.Print(LogClass.Application, "Loading as XCI."); + + if (!Device.LoadXci(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + + break; + } + case ".nca": + { + Logger.Info?.Print(LogClass.Application, "Loading as NCA."); + + if (!Device.LoadNca(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + + break; + } + case ".nsp": + case ".pfs0": + { + Logger.Info?.Print(LogClass.Application, "Loading as NSP."); + + if (!Device.LoadNsp(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + + break; + } + default: + { + Logger.Info?.Print(LogClass.Application, "Loading as homebrew."); + + try + { + if (!Device.LoadProgram(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + } + catch (ArgumentOutOfRangeException) + { + Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); + + Device.Dispose(); + + return false; + } + + break; + } + } + } + else + { + Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); + + Device.Dispose(); + + return false; + } + + DiscordIntegrationModule.SwitchToPlayingState(Device.Processes.ActiveApplication.ProgramIdText, Device.Processes.ActiveApplication.Name); + + ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => + { + appMetadata.UpdatePreGame(); + }); + + return true; + } + + internal void Resume() + { + Device?.System.TogglePauseEmulation(false); + + _viewModel.IsPaused = false; + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version); + Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); + } + + internal void Pause() + { + Device?.System.TogglePauseEmulation(true); + + _viewModel.IsPaused = true; + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, LocaleManager.Instance[LocaleKeys.Paused]); + Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); + } + + private void InitializeSwitchInstance() + { + // Initialize KeySet. + VirtualFileSystem.ReloadKeySet(); + + // Initialize Renderer. + IRenderer renderer; + + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) + { + renderer = new VulkanRenderer( + Vk.GetApi(), + (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, + VulkanHelper.GetRequiredInstanceExtensions, + ConfigurationState.Instance.Graphics.PreferredGpu.Value); + } + else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal) + { + renderer = new MetalRenderer(); + } + else + { + renderer = new OpenGLRenderer(); + } + + BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; + + var isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); + if (isGALThreaded) + { + renderer = new ThreadedRenderer(renderer); + } + + Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}"); + + // Initialize Configuration. + var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB; + + HLEConfiguration configuration = new(VirtualFileSystem, + _viewModel.LibHacHorizonManager, + ContentManager, + _accountManager, + _userChannelPersistence, + renderer, + InitializeAudio(), + memoryConfiguration, + _viewModel.UiHandler, + (SystemLanguage)ConfigurationState.Instance.System.Language.Value, + (RegionCode)ConfigurationState.Instance.System.Region.Value, + ConfigurationState.Instance.Graphics.EnableVsync, + ConfigurationState.Instance.System.EnableDockedMode, + ConfigurationState.Instance.System.EnablePtc, + ConfigurationState.Instance.System.EnableInternetAccess, + ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, + ConfigurationState.Instance.System.FsGlobalAccessLogMode, + ConfigurationState.Instance.System.SystemTimeOffset, + ConfigurationState.Instance.System.TimeZone, + ConfigurationState.Instance.System.MemoryManagerMode, + ConfigurationState.Instance.System.IgnoreMissingServices, + ConfigurationState.Instance.Graphics.AspectRatio, + ConfigurationState.Instance.System.AudioVolume, + ConfigurationState.Instance.System.UseHypervisor, + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, + ConfigurationState.Instance.Multiplayer.Mode); + + Device = new Switch(configuration); + } + + private static IHardwareDeviceDriver InitializeAudio() + { + var availableBackends = new List + { + AudioBackend.SDL2, + AudioBackend.SoundIo, + AudioBackend.OpenAl, + AudioBackend.Dummy, + }; + + AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value; + + for (int i = 0; i < availableBackends.Count; i++) + { + if (availableBackends[i] == preferredBackend) + { + availableBackends.RemoveAt(i); + availableBackends.Insert(0, preferredBackend); + break; + } + } + + static IHardwareDeviceDriver InitializeAudioBackend(AudioBackend backend, AudioBackend nextBackend) where T : IHardwareDeviceDriver, new() + { + if (T.IsSupported) + { + return new T(); + } + + Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}."); + + return null; + } + + IHardwareDeviceDriver deviceDriver = null; + + for (int i = 0; i < availableBackends.Count; i++) + { + AudioBackend currentBackend = availableBackends[i]; + AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy; + + deviceDriver = currentBackend switch + { + AudioBackend.SDL2 => InitializeAudioBackend(AudioBackend.SDL2, nextBackend), + AudioBackend.SoundIo => InitializeAudioBackend(AudioBackend.SoundIo, nextBackend), + AudioBackend.OpenAl => InitializeAudioBackend(AudioBackend.OpenAl, nextBackend), + _ => new DummyHardwareDeviceDriver(), + }; + + if (deviceDriver != null) + { + ConfigurationState.Instance.System.AudioBackend.Value = currentBackend; + break; + } + } + + MainWindowViewModel.SaveConfig(); + + return deviceDriver; + } + + private void Window_BoundsChanged(object sender, Size e) + { + Width = (int)e.Width; + Height = (int)e.Height; + + SetRendererWindowSize(e); + } + + private void MainLoop() + { + while (_isActive) + { + UpdateFrame(); + + // Polling becomes expensive if it's not slept. + Thread.Sleep(1); + } + } + + private void RenderLoop() + { + Dispatcher.UIThread.InvokeAsync(() => + { + if (_viewModel.StartGamesInFullscreen) + { + _viewModel.WindowState = WindowState.FullScreen; + } + + if (_viewModel.WindowState == WindowState.FullScreen) + { + _viewModel.ShowMenuAndStatusBar = false; + } + }); + + _renderer = Device.Gpu.Renderer is ThreadedRenderer tr ? tr.BaseRenderer : Device.Gpu.Renderer; + + _renderer.ScreenCaptured += Renderer_ScreenCaptured; + + (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer); + + Device.Gpu.Renderer.Initialize(_glLogLevel); + + _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); + _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + _renderer?.Window?.SetColorSpacePassthrough(ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); + + Width = (int)RendererHost.Bounds.Width; + Height = (int)RendererHost.Bounds.Height; + + _renderer.Window.SetSize((int)(Width * _topLevel.RenderScaling), (int)(Height * _topLevel.RenderScaling)); + + _chrono.Start(); + + Device.Gpu.Renderer.RunLoop(() => + { + Device.Gpu.SetGpuThread(); + Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); + + _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + + while (_isActive) + { + _ticks += _chrono.ElapsedTicks; + + _chrono.Restart(); + + if (Device.WaitFifo()) + { + Device.Statistics.RecordFifoStart(); + Device.ProcessFrame(); + Device.Statistics.RecordFifoEnd(); + } + + while (Device.ConsumeFrameAvailable()) + { + if (!_renderingStarted) + { + _renderingStarted = true; + _viewModel.SwitchToRenderer(false); + InitStatus(); + } + + Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); + } + + if (_ticks >= _ticksPerFrame) + { + UpdateStatus(); + } + } + + // Make sure all commands in the run loop are fully executed before leaving the loop. + if (Device.Gpu.Renderer is ThreadedRenderer threaded) + { + threaded.FlushThreadedCommands(); + } + + _gpuDoneEvent.Set(); + }); + + (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); + } + + public void InitStatus() + { + StatusInitEvent?.Invoke(this, new StatusInitEventArgs( + ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch + { + GraphicsBackend.Vulkan => "Vulkan", + GraphicsBackend.OpenGl => "OpenGL", + _ => throw new NotImplementedException() + }, + $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); + } + + public void UpdateStatus() + { + // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. + string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; + + if (GraphicsConfig.ResScale != 1) + { + dockedMode += $" ({GraphicsConfig.ResScale}x)"; + } + + StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( + Device.EnableDeviceVsync, + LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", +<<<<<<< HEAD +======= + ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToText(), +>>>>>>> 2daba02b8 (Start Metal Backend) + dockedMode, + ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), + LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", + $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %")); + } + + public async Task ShowExitPrompt() + { + bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit; + if (!shouldExit) + { + if (_dialogShown) + { + return; + } + + _dialogShown = true; + + shouldExit = await ContentDialogHelper.CreateStopEmulationDialog(); + + _dialogShown = false; + } + + if (shouldExit) + { + Stop(); + } + } + + private bool UpdateFrame() + { + if (!_isActive) + { + return false; + } + + NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); + + if (_viewModel.IsActive) + { + if (_isCursorInRenderer) + { + if (ConfigurationState.Instance.Hid.EnableMouse) + { + HideCursor(); + } + else + { + switch (ConfigurationState.Instance.HideCursor.Value) + { + case HideCursorMode.Never: + ShowCursor(); + break; + case HideCursorMode.OnIdle: + if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency) + { + HideCursor(); + } + else + { + ShowCursor(); + } + break; + case HideCursorMode.Always: + HideCursor(); + break; + } + } + } + else + { + ShowCursor(); + } + + Dispatcher.UIThread.Post(() => + { + if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen) + { + Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); + } + }); + + KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); + + if (currentHotkeyState != _prevHotkeyState) + { + switch (currentHotkeyState) + { + case KeyboardHotkeyState.ToggleVSync: + ToggleVSync(); + break; + case KeyboardHotkeyState.Screenshot: + ScreenshotRequested = true; + break; + case KeyboardHotkeyState.ShowUI: + _viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar; + break; + case KeyboardHotkeyState.Pause: + if (_viewModel.IsPaused) + { + Resume(); + } + else + { + Pause(); + } + break; + case KeyboardHotkeyState.ToggleMute: + if (Device.IsAudioMuted()) + { + Device.SetVolume(_viewModel.VolumeBeforeMute); + } + else + { + _viewModel.VolumeBeforeMute = Device.GetVolume(); + Device.SetVolume(0); + } + + _viewModel.Volume = Device.GetVolume(); + break; + case KeyboardHotkeyState.ResScaleUp: + GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; + break; + case KeyboardHotkeyState.ResScaleDown: + GraphicsConfig.ResScale = + (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; + break; + case KeyboardHotkeyState.VolumeUp: + _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); + Device.SetVolume(_newVolume); + + _viewModel.Volume = Device.GetVolume(); + break; + case KeyboardHotkeyState.VolumeDown: + _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); + Device.SetVolume(_newVolume); + + _viewModel.Volume = Device.GetVolume(); + break; + case KeyboardHotkeyState.None: + (_keyboardInterface as AvaloniaKeyboard).Clear(); + break; + } + } + + _prevHotkeyState = currentHotkeyState; + + if (ScreenshotRequested) + { + ScreenshotRequested = false; + _renderer.Screenshot(); + } + } + + // Touchscreen. + bool hasTouch = false; + + if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) + { + hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); + } + + if (!hasTouch) + { + Device.Hid.Touchscreen.Update(); + } + + Device.Hid.DebugPad.Update(); + + return true; + } + + private KeyboardHotkeyState GetHotkeyState() + { + KeyboardHotkeyState state = KeyboardHotkeyState.None; + + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + { + state = KeyboardHotkeyState.ToggleVSync; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) + { + state = KeyboardHotkeyState.Screenshot; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI)) + { + state = KeyboardHotkeyState.ShowUI; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) + { + state = KeyboardHotkeyState.Pause; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) + { + state = KeyboardHotkeyState.ToggleMute; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) + { + state = KeyboardHotkeyState.ResScaleUp; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) + { + state = KeyboardHotkeyState.ResScaleDown; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) + { + state = KeyboardHotkeyState.VolumeUp; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) + { + state = KeyboardHotkeyState.VolumeDown; + } + + return state; + } + } +} diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 05fd66b90..3c24b8e27 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -198,6 +198,7 @@ namespace Ryujinx.Ava { "opengl" => GraphicsBackend.OpenGl, "vulkan" => GraphicsBackend.Vulkan, + "metal" => GraphicsBackend.Metal, _ => ConfigurationState.Instance.Graphics.GraphicsBackend }; diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs new file mode 100644 index 000000000..a8bac75c0 --- /dev/null +++ b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs @@ -0,0 +1,25 @@ +using SPB.Windowing; +using SPB.Platform.Metal; +using System; + +namespace Ryujinx.UI.Renderer +{ + public class EmbeddedWindowMetal : EmbeddedWindow + { + public SimpleMetalWindow CreateSurface() + { + SimpleMetalWindow simpleMetalWindow; + + if (OperatingSystem.IsMacOS()) + { + simpleMetalWindow = new SimpleMetalWindow(new NativeHandle(NsView), new NativeHandle(MetalLayer)); + } + else + { + throw new PlatformNotSupportedException(); + } + + return simpleMetalWindow; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 0824e3f86..8b7c6d967 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -120,6 +120,8 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public bool IsMetalAvailable => OperatingSystem.IsMacOS(); + public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS(); public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index 219efcef8..62c083d15 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -43,6 +43,9 @@ + + + -- 2.47.1 From 6d513cad1ed322fbe55a84a0cb6734484e3b1c15 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 20:36:29 -0600 Subject: [PATCH 002/368] merging leftovers --- src/Ryujinx/AppHost.cs.orig | 1225 ----------------------------------- 1 file changed, 1225 deletions(-) delete mode 100644 src/Ryujinx/AppHost.cs.orig diff --git a/src/Ryujinx/AppHost.cs.orig b/src/Ryujinx/AppHost.cs.orig deleted file mode 100644 index 99663fbc5..000000000 --- a/src/Ryujinx/AppHost.cs.orig +++ /dev/null @@ -1,1225 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Input; -using Avalonia.Threading; -using LibHac.Tools.FsSystem; -using Ryujinx.Audio.Backends.Dummy; -using Ryujinx.Audio.Backends.OpenAL; -using Ryujinx.Audio.Backends.SDL2; -using Ryujinx.Audio.Backends.SoundIo; -using Ryujinx.Audio.Integration; -using Ryujinx.Ava.Common; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Input; -using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.UI.Renderer; -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Ava.UI.Windows; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Multiplayer; -using Ryujinx.Common.Logging; -using Ryujinx.Common.SystemInterop; -using Ryujinx.Common.Utilities; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.Graphics.Gpu; -using Ryujinx.Graphics.OpenGL; -using Ryujinx.Graphics.Vulkan; -using Ryujinx.Graphics.Metal; -using Ryujinx.HLE; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.Input; -using Ryujinx.Input.HLE; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Silk.NET.Vulkan; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SPB.Graphics.Vulkan; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; -using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; -using Image = SixLabors.ImageSharp.Image; -using InputManager = Ryujinx.Input.HLE.InputManager; -using IRenderer = Ryujinx.Graphics.GAL.IRenderer; -using Key = Ryujinx.Input.Key; -using MouseButton = Ryujinx.Input.MouseButton; -using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; -using Size = Avalonia.Size; -using Switch = Ryujinx.HLE.Switch; - -namespace Ryujinx.Ava -{ - internal class AppHost - { - private const int CursorHideIdleTime = 5; // Hide Cursor seconds. - private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. - private const int TargetFps = 60; - private const float VolumeDelta = 0.05f; - - private static readonly Cursor _invisibleCursor = new(StandardCursorType.None); - private readonly IntPtr _invisibleCursorWin; - private readonly IntPtr _defaultCursorWin; - - private readonly long _ticksPerFrame; - private readonly Stopwatch _chrono; - private long _ticks; - - private readonly AccountManager _accountManager; - private readonly UserChannelPersistence _userChannelPersistence; - private readonly InputManager _inputManager; - - private readonly MainWindowViewModel _viewModel; - private readonly IKeyboard _keyboardInterface; - private readonly TopLevel _topLevel; - public RendererHost RendererHost; - - private readonly GraphicsDebugLevel _glLogLevel; - private float _newVolume; - private KeyboardHotkeyState _prevHotkeyState; - - private long _lastCursorMoveTime; - private bool _isCursorInRenderer = true; - - private bool _isStopped; - private bool _isActive; - private bool _renderingStarted; - - private readonly ManualResetEvent _gpuDoneEvent; - - private IRenderer _renderer; - private readonly Thread _renderingThread; - private readonly CancellationTokenSource _gpuCancellationTokenSource; - private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; - - private bool _dialogShown; - private readonly bool _isFirmwareTitle; - - private readonly object _lockObject = new(); - - public event EventHandler AppExit; - public event EventHandler StatusInitEvent; - public event EventHandler StatusUpdatedEvent; - - public VirtualFileSystem VirtualFileSystem { get; } - public ContentManager ContentManager { get; } - public NpadManager NpadManager { get; } - public TouchScreenManager TouchScreenManager { get; } - public Switch Device { get; set; } - - public int Width { get; private set; } - public int Height { get; private set; } - public string ApplicationPath { get; private set; } - public bool ScreenshotRequested { get; set; } - - public AppHost( - RendererHost renderer, - InputManager inputManager, - string applicationPath, - VirtualFileSystem virtualFileSystem, - ContentManager contentManager, - AccountManager accountManager, - UserChannelPersistence userChannelPersistence, - MainWindowViewModel viewmodel, - TopLevel topLevel) - { - _viewModel = viewmodel; - _inputManager = inputManager; - _accountManager = accountManager; - _userChannelPersistence = userChannelPersistence; - _renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" }; - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; - _topLevel = topLevel; - - _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer)); - - _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); - - NpadManager = _inputManager.CreateNpadManager(); - TouchScreenManager = _inputManager.CreateTouchScreenManager(); - ApplicationPath = applicationPath; - VirtualFileSystem = virtualFileSystem; - ContentManager = contentManager; - - RendererHost = renderer; - - _chrono = new Stopwatch(); - _ticksPerFrame = Stopwatch.Frequency / TargetFps; - - if (ApplicationPath.StartsWith("@SystemContent")) - { - ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); - - _isFirmwareTitle = true; - } - - ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; - - _topLevel.PointerMoved += TopLevel_PointerEnteredOrMoved; - _topLevel.PointerEntered += TopLevel_PointerEnteredOrMoved; - _topLevel.PointerExited += TopLevel_PointerExited; - - if (OperatingSystem.IsWindows()) - { - _invisibleCursorWin = CreateEmptyCursor(); - _defaultCursorWin = CreateArrowCursor(); - } - - ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; - ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; - ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; - ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough; - - ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState; - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; - ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState; - - _gpuCancellationTokenSource = new CancellationTokenSource(); - _gpuDoneEvent = new ManualResetEvent(false); - } - - private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e) - { - if (sender is MainWindow window) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - - var point = e.GetCurrentPoint(window).Position; - var bounds = RendererHost.EmbeddedWindow.Bounds; - - _isCursorInRenderer = point.X >= bounds.X && - point.X <= bounds.Width + bounds.X && - point.Y >= bounds.Y && - point.Y <= bounds.Height + bounds.Y; - } - } - - private void TopLevel_PointerExited(object sender, PointerEventArgs e) - { - _isCursorInRenderer = false; - } - - private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs e) - { - _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - } - - private void UpdateScalingFilter(object sender, ReactiveEventArgs e) - { - _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - } - - private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs e) - { - _renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); - } - - private void ShowCursor() - { - Dispatcher.UIThread.Post(() => - { - _viewModel.Cursor = Cursor.Default; - - if (OperatingSystem.IsWindows()) - { - SetCursor(_defaultCursorWin); - } - }); - } - - private void HideCursor() - { - Dispatcher.UIThread.Post(() => - { - _viewModel.Cursor = _invisibleCursor; - - if (OperatingSystem.IsWindows()) - { - SetCursor(_invisibleCursorWin); - } - }); - } - - private void SetRendererWindowSize(Size size) - { - if (_renderer != null) - { - double scale = _topLevel.RenderScaling; - - _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); - } - } - - private void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e) - { - if (e.Data.Length > 0 && e.Height > 0 && e.Width > 0) - { - Task.Run(() => - { - lock (_lockObject) - { - string applicationName = Device.Processes.ActiveApplication.Name; - string sanitizedApplicationName = FileSystemUtils.SanitizeFileName(applicationName); - DateTime currentTime = DateTime.Now; - - string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; - - string directory = AppDataManager.Mode switch - { - AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), - _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), - }; - - string path = Path.Combine(directory, filename); - - try - { - Directory.CreateDirectory(directory); - } - catch (Exception ex) - { - Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {directory}. Error : {ex.GetType().Name}", "Screenshot"); - - return; - } - - Image image = e.IsBgra ? Image.LoadPixelData(e.Data, e.Width, e.Height) - : Image.LoadPixelData(e.Data, e.Width, e.Height); - - if (e.FlipX) - { - image.Mutate(x => x.Flip(FlipMode.Horizontal)); - } - - if (e.FlipY) - { - image.Mutate(x => x.Flip(FlipMode.Vertical)); - } - - image.SaveAsPng(path, new PngEncoder - { - ColorType = PngColorType.Rgb, - }); - - image.Dispose(); - - Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); - } - }); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Screenshot is empty. Size : {e.Data.Length} bytes. Resolution : {e.Width}x{e.Height}", "Screenshot"); - } - } - - public void Start() - { - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); - } - - DisplaySleep.Prevent(); - - NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); - TouchScreenManager.Initialize(Device); - - _viewModel.IsGameRunning = true; - - Dispatcher.UIThread.InvokeAsync(() => - { - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version); - }); - - _viewModel.SetUiProgressHandlers(Device); - - RendererHost.BoundsChanged += Window_BoundsChanged; - - _isActive = true; - - _renderingThread.Start(); - - _viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value; - - MainLoop(); - - Exit(); - } - - private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args) - { - if (Device != null) - { - Device.Configuration.IgnoreMissingServices = args.NewValue; - } - } - - private void UpdateAspectRatioState(object sender, ReactiveEventArgs args) - { - if (Device != null) - { - Device.Configuration.AspectRatio = args.NewValue; - } - } - - private void UpdateAntiAliasing(object sender, ReactiveEventArgs e) - { - _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); - } - - private void UpdateDockedModeState(object sender, ReactiveEventArgs e) - { - Device?.System.ChangeDockedModeState(e.NewValue); - } - - private void UpdateAudioVolumeState(object sender, ReactiveEventArgs e) - { - Device?.SetVolume(e.NewValue); - - Dispatcher.UIThread.Post(() => - { - _viewModel.Volume = e.NewValue; - }); - } - - private void UpdateEnableInternetAccessState(object sender, ReactiveEventArgs e) - { - Device.Configuration.EnableInternetAccess = e.NewValue; - } - - private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs e) - { - Device.Configuration.MultiplayerLanInterfaceId = e.NewValue; - } - - private void UpdateMultiplayerModeState(object sender, ReactiveEventArgs e) - { - Device.Configuration.MultiplayerMode = e.NewValue; - } - - public void ToggleVSync() - { - Device.EnableDeviceVsync = !Device.EnableDeviceVsync; - _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); - } - - public void Stop() - { - _isActive = false; - } - - private void Exit() - { - (_keyboardInterface as AvaloniaKeyboard)?.Clear(); - - if (_isStopped) - { - return; - } - - _isStopped = true; - _isActive = false; - } - - public void DisposeContext() - { - Dispose(); - - _isActive = false; - - // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. - // We only need to wait for all commands submitted during the main gpu loop to be processed. - _gpuDoneEvent.WaitOne(); - _gpuDoneEvent.Dispose(); - - DisplaySleep.Restore(); - - NpadManager.Dispose(); - TouchScreenManager.Dispose(); - Device.Dispose(); - - DisposeGpu(); - - AppExit?.Invoke(this, EventArgs.Empty); - } - - private void Dispose() - { - if (Device.Processes != null) - { - MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); - } - - ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; - ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; - ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; - ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough; - - _topLevel.PointerMoved -= TopLevel_PointerEnteredOrMoved; - _topLevel.PointerEntered -= TopLevel_PointerEnteredOrMoved; - _topLevel.PointerExited -= TopLevel_PointerExited; - - _gpuCancellationTokenSource.Cancel(); - _gpuCancellationTokenSource.Dispose(); - - _chrono.Stop(); - } - - public void DisposeGpu() - { - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution?.Dispose(); - _windowsMultimediaTimerResolution = null; - } - - if (RendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow) - { - // Try to bind the OpenGL context before calling the shutdown event. - openGlWindow.MakeCurrent(false, false); - - Device.DisposeGpu(); - - // Unbind context and destroy everything. - openGlWindow.MakeCurrent(true, false); - } - else - { - Device.DisposeGpu(); - } - } - - private void HideCursorState_Changed(object sender, ReactiveEventArgs state) - { - if (state.NewValue == HideCursorMode.OnIdle) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - } - } - - public async Task LoadGuestApplication() - { - InitializeSwitchInstance(); - MainWindow.UpdateGraphicsConfig(); - - SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) - { - { - if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) - { - if (userError == UserError.NoFirmware) - { - UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString), - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - ""); - - if (result != UserResult.Yes) - { - await UserErrorDialog.ShowUserErrorDialog(userError); - Device.Dispose(); - - return false; - } - } - - if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) - { - await UserErrorDialog.ShowUserErrorDialog(userError); - Device.Dispose(); - - return false; - } - - // Tell the user that we installed a firmware for them. - if (userError == UserError.NoFirmware) - { - firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - - _viewModel.RefreshFirmwareStatus(); - - await ContentDialogHelper.CreateInfoDialog( - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString), - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString), - LocaleManager.Instance[LocaleKeys.InputDialogOk], - "", - LocaleManager.Instance[LocaleKeys.RyujinxInfo]); - } - } - else - { - await UserErrorDialog.ShowUserErrorDialog(userError); - Device.Dispose(); - - return false; - } - } - } - } - - Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); - - if (_isFirmwareTitle) - { - Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); - - if (!Device.LoadNca(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - } - else if (Directory.Exists(ApplicationPath)) - { - string[] romFsFiles = Directory.GetFiles(ApplicationPath, "*.istorage"); - - if (romFsFiles.Length == 0) - { - romFsFiles = Directory.GetFiles(ApplicationPath, "*.romfs"); - } - - if (romFsFiles.Length > 0) - { - Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); - - if (!Device.LoadCart(ApplicationPath, romFsFiles[0])) - { - Device.Dispose(); - - return false; - } - } - else - { - Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); - - if (!Device.LoadCart(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - } - } - else if (File.Exists(ApplicationPath)) - { - switch (Path.GetExtension(ApplicationPath).ToLowerInvariant()) - { - case ".xci": - { - Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - - if (!Device.LoadXci(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - - break; - } - case ".nca": - { - Logger.Info?.Print(LogClass.Application, "Loading as NCA."); - - if (!Device.LoadNca(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - - break; - } - case ".nsp": - case ".pfs0": - { - Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - - if (!Device.LoadNsp(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - - break; - } - default: - { - Logger.Info?.Print(LogClass.Application, "Loading as homebrew."); - - try - { - if (!Device.LoadProgram(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - } - catch (ArgumentOutOfRangeException) - { - Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); - - Device.Dispose(); - - return false; - } - - break; - } - } - } - else - { - Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); - - Device.Dispose(); - - return false; - } - - DiscordIntegrationModule.SwitchToPlayingState(Device.Processes.ActiveApplication.ProgramIdText, Device.Processes.ActiveApplication.Name); - - ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => - { - appMetadata.UpdatePreGame(); - }); - - return true; - } - - internal void Resume() - { - Device?.System.TogglePauseEmulation(false); - - _viewModel.IsPaused = false; - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version); - Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); - } - - internal void Pause() - { - Device?.System.TogglePauseEmulation(true); - - _viewModel.IsPaused = true; - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, LocaleManager.Instance[LocaleKeys.Paused]); - Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); - } - - private void InitializeSwitchInstance() - { - // Initialize KeySet. - VirtualFileSystem.ReloadKeySet(); - - // Initialize Renderer. - IRenderer renderer; - - if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) - { - renderer = new VulkanRenderer( - Vk.GetApi(), - (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, - VulkanHelper.GetRequiredInstanceExtensions, - ConfigurationState.Instance.Graphics.PreferredGpu.Value); - } - else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal) - { - renderer = new MetalRenderer(); - } - else - { - renderer = new OpenGLRenderer(); - } - - BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - - var isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - if (isGALThreaded) - { - renderer = new ThreadedRenderer(renderer); - } - - Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}"); - - // Initialize Configuration. - var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB; - - HLEConfiguration configuration = new(VirtualFileSystem, - _viewModel.LibHacHorizonManager, - ContentManager, - _accountManager, - _userChannelPersistence, - renderer, - InitializeAudio(), - memoryConfiguration, - _viewModel.UiHandler, - (SystemLanguage)ConfigurationState.Instance.System.Language.Value, - (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.EnableVsync, - ConfigurationState.Instance.System.EnableDockedMode, - ConfigurationState.Instance.System.EnablePtc, - ConfigurationState.Instance.System.EnableInternetAccess, - ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, - ConfigurationState.Instance.System.FsGlobalAccessLogMode, - ConfigurationState.Instance.System.SystemTimeOffset, - ConfigurationState.Instance.System.TimeZone, - ConfigurationState.Instance.System.MemoryManagerMode, - ConfigurationState.Instance.System.IgnoreMissingServices, - ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume, - ConfigurationState.Instance.System.UseHypervisor, - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, - ConfigurationState.Instance.Multiplayer.Mode); - - Device = new Switch(configuration); - } - - private static IHardwareDeviceDriver InitializeAudio() - { - var availableBackends = new List - { - AudioBackend.SDL2, - AudioBackend.SoundIo, - AudioBackend.OpenAl, - AudioBackend.Dummy, - }; - - AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value; - - for (int i = 0; i < availableBackends.Count; i++) - { - if (availableBackends[i] == preferredBackend) - { - availableBackends.RemoveAt(i); - availableBackends.Insert(0, preferredBackend); - break; - } - } - - static IHardwareDeviceDriver InitializeAudioBackend(AudioBackend backend, AudioBackend nextBackend) where T : IHardwareDeviceDriver, new() - { - if (T.IsSupported) - { - return new T(); - } - - Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}."); - - return null; - } - - IHardwareDeviceDriver deviceDriver = null; - - for (int i = 0; i < availableBackends.Count; i++) - { - AudioBackend currentBackend = availableBackends[i]; - AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy; - - deviceDriver = currentBackend switch - { - AudioBackend.SDL2 => InitializeAudioBackend(AudioBackend.SDL2, nextBackend), - AudioBackend.SoundIo => InitializeAudioBackend(AudioBackend.SoundIo, nextBackend), - AudioBackend.OpenAl => InitializeAudioBackend(AudioBackend.OpenAl, nextBackend), - _ => new DummyHardwareDeviceDriver(), - }; - - if (deviceDriver != null) - { - ConfigurationState.Instance.System.AudioBackend.Value = currentBackend; - break; - } - } - - MainWindowViewModel.SaveConfig(); - - return deviceDriver; - } - - private void Window_BoundsChanged(object sender, Size e) - { - Width = (int)e.Width; - Height = (int)e.Height; - - SetRendererWindowSize(e); - } - - private void MainLoop() - { - while (_isActive) - { - UpdateFrame(); - - // Polling becomes expensive if it's not slept. - Thread.Sleep(1); - } - } - - private void RenderLoop() - { - Dispatcher.UIThread.InvokeAsync(() => - { - if (_viewModel.StartGamesInFullscreen) - { - _viewModel.WindowState = WindowState.FullScreen; - } - - if (_viewModel.WindowState == WindowState.FullScreen) - { - _viewModel.ShowMenuAndStatusBar = false; - } - }); - - _renderer = Device.Gpu.Renderer is ThreadedRenderer tr ? tr.BaseRenderer : Device.Gpu.Renderer; - - _renderer.ScreenCaptured += Renderer_ScreenCaptured; - - (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer); - - Device.Gpu.Renderer.Initialize(_glLogLevel); - - _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); - _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - _renderer?.Window?.SetColorSpacePassthrough(ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); - - Width = (int)RendererHost.Bounds.Width; - Height = (int)RendererHost.Bounds.Height; - - _renderer.Window.SetSize((int)(Width * _topLevel.RenderScaling), (int)(Height * _topLevel.RenderScaling)); - - _chrono.Start(); - - Device.Gpu.Renderer.RunLoop(() => - { - Device.Gpu.SetGpuThread(); - Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); - - _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); - - while (_isActive) - { - _ticks += _chrono.ElapsedTicks; - - _chrono.Restart(); - - if (Device.WaitFifo()) - { - Device.Statistics.RecordFifoStart(); - Device.ProcessFrame(); - Device.Statistics.RecordFifoEnd(); - } - - while (Device.ConsumeFrameAvailable()) - { - if (!_renderingStarted) - { - _renderingStarted = true; - _viewModel.SwitchToRenderer(false); - InitStatus(); - } - - Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); - } - - if (_ticks >= _ticksPerFrame) - { - UpdateStatus(); - } - } - - // Make sure all commands in the run loop are fully executed before leaving the loop. - if (Device.Gpu.Renderer is ThreadedRenderer threaded) - { - threaded.FlushThreadedCommands(); - } - - _gpuDoneEvent.Set(); - }); - - (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); - } - - public void InitStatus() - { - StatusInitEvent?.Invoke(this, new StatusInitEventArgs( - ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch - { - GraphicsBackend.Vulkan => "Vulkan", - GraphicsBackend.OpenGl => "OpenGL", - _ => throw new NotImplementedException() - }, - $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); - } - - public void UpdateStatus() - { - // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. - string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; - - if (GraphicsConfig.ResScale != 1) - { - dockedMode += $" ({GraphicsConfig.ResScale}x)"; - } - - StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, - LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", -<<<<<<< HEAD -======= - ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToText(), ->>>>>>> 2daba02b8 (Start Metal Backend) - dockedMode, - ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", - $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %")); - } - - public async Task ShowExitPrompt() - { - bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit; - if (!shouldExit) - { - if (_dialogShown) - { - return; - } - - _dialogShown = true; - - shouldExit = await ContentDialogHelper.CreateStopEmulationDialog(); - - _dialogShown = false; - } - - if (shouldExit) - { - Stop(); - } - } - - private bool UpdateFrame() - { - if (!_isActive) - { - return false; - } - - NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - - if (_viewModel.IsActive) - { - if (_isCursorInRenderer) - { - if (ConfigurationState.Instance.Hid.EnableMouse) - { - HideCursor(); - } - else - { - switch (ConfigurationState.Instance.HideCursor.Value) - { - case HideCursorMode.Never: - ShowCursor(); - break; - case HideCursorMode.OnIdle: - if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency) - { - HideCursor(); - } - else - { - ShowCursor(); - } - break; - case HideCursorMode.Always: - HideCursor(); - break; - } - } - } - else - { - ShowCursor(); - } - - Dispatcher.UIThread.Post(() => - { - if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen) - { - Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); - } - }); - - KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); - - if (currentHotkeyState != _prevHotkeyState) - { - switch (currentHotkeyState) - { - case KeyboardHotkeyState.ToggleVSync: - ToggleVSync(); - break; - case KeyboardHotkeyState.Screenshot: - ScreenshotRequested = true; - break; - case KeyboardHotkeyState.ShowUI: - _viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar; - break; - case KeyboardHotkeyState.Pause: - if (_viewModel.IsPaused) - { - Resume(); - } - else - { - Pause(); - } - break; - case KeyboardHotkeyState.ToggleMute: - if (Device.IsAudioMuted()) - { - Device.SetVolume(_viewModel.VolumeBeforeMute); - } - else - { - _viewModel.VolumeBeforeMute = Device.GetVolume(); - Device.SetVolume(0); - } - - _viewModel.Volume = Device.GetVolume(); - break; - case KeyboardHotkeyState.ResScaleUp: - GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; - break; - case KeyboardHotkeyState.ResScaleDown: - GraphicsConfig.ResScale = - (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; - break; - case KeyboardHotkeyState.VolumeUp: - _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); - Device.SetVolume(_newVolume); - - _viewModel.Volume = Device.GetVolume(); - break; - case KeyboardHotkeyState.VolumeDown: - _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); - Device.SetVolume(_newVolume); - - _viewModel.Volume = Device.GetVolume(); - break; - case KeyboardHotkeyState.None: - (_keyboardInterface as AvaloniaKeyboard).Clear(); - break; - } - } - - _prevHotkeyState = currentHotkeyState; - - if (ScreenshotRequested) - { - ScreenshotRequested = false; - _renderer.Screenshot(); - } - } - - // Touchscreen. - bool hasTouch = false; - - if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) - { - hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - } - - if (!hasTouch) - { - Device.Hid.Touchscreen.Update(); - } - - Device.Hid.DebugPad.Update(); - - return true; - } - - private KeyboardHotkeyState GetHotkeyState() - { - KeyboardHotkeyState state = KeyboardHotkeyState.None; - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) - { - state = KeyboardHotkeyState.ToggleVSync; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) - { - state = KeyboardHotkeyState.Screenshot; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI)) - { - state = KeyboardHotkeyState.ShowUI; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) - { - state = KeyboardHotkeyState.Pause; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) - { - state = KeyboardHotkeyState.ToggleMute; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) - { - state = KeyboardHotkeyState.ResScaleUp; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) - { - state = KeyboardHotkeyState.ResScaleDown; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) - { - state = KeyboardHotkeyState.VolumeUp; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) - { - state = KeyboardHotkeyState.VolumeDown; - } - - return state; - } - } -} -- 2.47.1 From 93e5ff0137c07451243b8f139092f356b0dee3ad Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 20:41:07 -0600 Subject: [PATCH 003/368] up to date with 08126b26b1db39b2eb11be81b4e2ffd613b0bdbf --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 139 ++++++++++++++---- .../RenderEncoderState.cs | 27 ++++ src/Ryujinx.Graphics.Metal/StringHelper.cs | 30 ++++ src/Ryujinx.Graphics.Metal/Texture.cs | 114 +++++++++++--- 4 files changed, 261 insertions(+), 49 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/RenderEncoderState.cs create mode 100644 src/Ryujinx.Graphics.Metal/StringHelper.cs diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index dd1a5e071..a0af3c33f 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using SharpMetal.Foundation; @@ -15,14 +16,11 @@ namespace Ryujinx.Graphics.Metal private readonly MTLCommandQueue _mtlCommandQueue; private MTLCommandBuffer _commandBuffer; - private MTLRenderCommandEncoder _renderCommandEncoder; - private MTLRenderPipelineState _renderPipelineState; - private MTLBlitCommandEncoder _blitCommandEncoder; + private MTLCommandEncoder _currentEncoder; - public MTLRenderCommandEncoder RenderCommandEncoder => _renderCommandEncoder; - public MTLBlitCommandEncoder BlitCommandEncoder => _blitCommandEncoder; + public MTLCommandEncoder CurrentEncoder; - private PrimitiveTopology _topology; + private RenderEncoderState _renderEncoderState; private MTLBuffer _indexBuffer; private MTLIndexType _indexType; @@ -35,35 +33,56 @@ namespace Ryujinx.Graphics.Metal var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); var error = new NSError(IntPtr.Zero); - _renderPipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + _renderEncoderState = new(_device.NewRenderPipelineState(renderPipelineDescriptor, ref error)); if (error != IntPtr.Zero) { - // throw new Exception($"Failed to create render pipeline state! {StringHelp}"); - throw new Exception($"Failed to create render pipeline state!"); + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); } - CreateCommandBuffer(); + _commandBuffer = _mtlCommandQueue.CommandBuffer(); + } + + public void EndCurrentPass() + { + if (_currentEncoder != null) + { + _currentEncoder.EndEncoding(); + _currentEncoder = null; + } + } + + public MTLRenderCommandEncoder BeginRenderPass() + { + EndCurrentPass(); + + var descriptor = new MTLRenderPassDescriptor { }; + var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); + _renderEncoderState.SetEncoderState(renderCommandEncoder); + + _currentEncoder = renderCommandEncoder; + return renderCommandEncoder; + } + + public MTLBlitCommandEncoder BeginBlitPass() + { + EndCurrentPass(); + + var descriptor = new MTLBlitPassDescriptor { }; + var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); + + _currentEncoder = blitCommandEncoder; + return blitCommandEncoder; } public void Present() { - _renderCommandEncoder.EndEncoding(); - _blitCommandEncoder.EndEncoding(); + EndCurrentPass(); + // TODO: Give command buffer a valid MTLDrawable // _commandBuffer.PresentDrawable(); _commandBuffer.Commit(); - CreateCommandBuffer(); - } - - public void CreateCommandBuffer() - { _commandBuffer = _mtlCommandQueue.CommandBuffer(); - - _renderCommandEncoder = _commandBuffer.RenderCommandEncoder(new MTLRenderPassDescriptor()); - _renderCommandEncoder.SetRenderPipelineState(_renderPipelineState); - - _blitCommandEncoder = _commandBuffer.BlitCommandEncoder(new MTLBlitPassDescriptor()); } public void Barrier() @@ -73,7 +92,27 @@ namespace Ryujinx.Graphics.Metal public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) { - throw new NotImplementedException(); + MTLBlitCommandEncoder blitCommandEncoder; + + if (_currentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = BeginBlitPass(); + } + + // Might need a closer look, range's count, lower, and upper bound + // must be a multiple of 4 + MTLBuffer mtlBuffer = new(Unsafe.As(ref destination)); + blitCommandEncoder.FillBuffer(mtlBuffer, + new NSRange + { + location = (ulong)offset, + length = (ulong)size + }, + (byte)value); } public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) @@ -104,18 +143,40 @@ namespace Ryujinx.Graphics.Metal public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { - // TODO: Support topology re-indexing to provide support for TriangleFans - var _primitiveType = _topology.Convert(); + MTLRenderCommandEncoder renderCommandEncoder; - _renderCommandEncoder.DrawPrimitives(_primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance); + if (_currentEncoder is MTLRenderCommandEncoder encoder) + { + renderCommandEncoder = encoder; + } + else + { + renderCommandEncoder = BeginRenderPass(); + } + + // TODO: Support topology re-indexing to provide support for TriangleFans + var primitiveType = _renderEncoderState.Topology.Convert(); + + renderCommandEncoder.DrawPrimitives(primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance); } public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) { - // TODO: Support topology re-indexing to provide support for TriangleFans - var _primitiveType = _topology.Convert(); + MTLRenderCommandEncoder renderCommandEncoder; - _renderCommandEncoder.DrawIndexedPrimitives(_primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance); + if (_currentEncoder is MTLRenderCommandEncoder encoder) + { + renderCommandEncoder = encoder; + } + else + { + renderCommandEncoder = BeginRenderPass(); + } + + // TODO: Support topology re-indexing to provide support for TriangleFans + var primitiveType = _renderEncoderState.Topology.Convert(); + + renderCommandEncoder.DrawIndexedPrimitives(primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance); } public void DrawIndexedIndirect(BufferRange indirectBuffer) @@ -180,12 +241,26 @@ namespace Ryujinx.Graphics.Metal public void SetFaceCulling(bool enable, Face face) { - _renderCommandEncoder.SetCullMode(enable ? face.Convert() : MTLCullMode.None); + var cullMode = enable ? face.Convert() : MTLCullMode.None; + + if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetCullMode(cullMode); + } + + _renderEncoderState.CullMode = cullMode; } public void SetFrontFace(FrontFace frontFace) { - _renderCommandEncoder.SetFrontFacingWinding(frontFace.Convert()); + var winding = frontFace.Convert(); + + if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetFrontFacingWinding(winding); + } + + _renderEncoderState.Winding = winding; } public void SetIndexBuffer(BufferRange buffer, IndexType type) @@ -244,7 +319,7 @@ namespace Ryujinx.Graphics.Metal public void SetPrimitiveTopology(PrimitiveTopology topology) { - _topology = topology; + _renderEncoderState.Topology = topology; } public void SetProgram(IProgram program) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs new file mode 100644 index 000000000..5f078ded6 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -0,0 +1,27 @@ +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + struct RenderEncoderState + { + public MTLRenderPipelineState RenderPipelineState; + public PrimitiveTopology Topology = PrimitiveTopology.Triangles; + public MTLCullMode CullMode = MTLCullMode.None; + public MTLWinding Winding = MTLWinding.Clockwise; + + public RenderEncoderState(MTLRenderPipelineState renderPipelineState) + { + RenderPipelineState = renderPipelineState; + } + + public void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetRenderPipelineState(RenderPipelineState); + renderCommandEncoder.SetCullMode(CullMode); + renderCommandEncoder.SetFrontFacingWinding(Winding); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/StringHelper.cs b/src/Ryujinx.Graphics.Metal/StringHelper.cs new file mode 100644 index 000000000..21cd474dc --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/StringHelper.cs @@ -0,0 +1,30 @@ +using SharpMetal.Foundation; +using SharpMetal.ObjectiveCCore; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class StringHelper + { + public static NSString NSString(string source) + { + return new(ObjectiveC.IntPtr_objc_msgSend(new ObjectiveCClass("NSString"), "stringWithUTF8String:", source)); + } + + public static unsafe string String(NSString source) + { + char[] sourceBuffer = new char[source.Length]; + fixed (char* pSourceBuffer = sourceBuffer) + { + ObjectiveC.bool_objc_msgSend(source, + "getCString:maxLength:encoding:", + pSourceBuffer, + source.MaximumLengthOfBytes(NSStringEncoding.UTF16) + 1, + (ulong)NSStringEncoding.UTF16); + } + + return new string(sourceBuffer); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index b03fb26c8..c976dba6d 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Metal private readonly MTLDevice _device; public MTLTexture MTLTexture; - public TextureCreateInfo Info => Info; + public TextureCreateInfo Info => _info; public int Width => Info.Width; public int Height => Info.Height; @@ -46,9 +46,20 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { + MTLBlitCommandEncoder blitCommandEncoder; + + if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = _pipeline.BeginBlitPass(); + } + if (destination is Texture destinationTexture) { - _pipeline.BlitCommandEncoder.CopyFromTexture( + blitCommandEncoder.CopyFromTexture( MTLTexture, (ulong)firstLayer, (ulong)firstLevel, @@ -62,9 +73,20 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { + MTLBlitCommandEncoder blitCommandEncoder; + + if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = _pipeline.BeginBlitPass(); + } + if (destination is Texture destinationTexture) { - _pipeline.BlitCommandEncoder.CopyFromTexture( + blitCommandEncoder.CopyFromTexture( MTLTexture, (ulong)srcLayer, (ulong)srcLevel, @@ -111,25 +133,83 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data, int layer, int level) { - throw new NotImplementedException(); + MTLBlitCommandEncoder blitCommandEncoder; + + if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = _pipeline.BeginBlitPass(); + } + + ulong bytesPerRow = (ulong)Info.GetMipStride(level); + ulong bytesPerImage = 0; + if (MTLTexture.TextureType == MTLTextureType.Type3D) + { + bytesPerImage = bytesPerRow * (ulong)Info.Height; + } + + unsafe + { + var dataSpan = data.Span; + var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); + var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); + dataSpan.CopyTo(bufferSpan); + + blitCommandEncoder.CopyFromBuffer( + mtlBuffer, + 0, + bytesPerRow, + bytesPerImage, + new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height }, + MTLTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin() + ); + } } - public unsafe void SetData(SpanOrArray data, int layer, int level, Rectangle region) + public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { - // TODO: Figure out bytesPerRow - // For an ordinary or packed pixel format, the stride, in bytes, between rows of source data. - // For a compressed pixel format, the stride is the number of bytes from the beginning of one row of blocks to the beginning of the next. - if (MTLTexture.IsSparse) - ulong bytesPerRow = 0; - var mtlRegion = new MTLRegion - { - origin = new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y }, - size = new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height }, - }; + MTLBlitCommandEncoder blitCommandEncoder; - fixed (byte* pData = data.Span) + if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) { - MTLTexture.ReplaceRegion(mtlRegion, (ulong)level, (ulong)layer, new IntPtr(pData), bytesPerRow, 0); + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = _pipeline.BeginBlitPass(); + } + + ulong bytesPerRow = (ulong)Info.GetMipStride(level); + ulong bytesPerImage = 0; + if (MTLTexture.TextureType == MTLTextureType.Type3D) + { + bytesPerImage = bytesPerRow * (ulong)Info.Height; + } + + unsafe + { + var dataSpan = data.Span; + var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); + var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); + dataSpan.CopyTo(bufferSpan); + + blitCommandEncoder.CopyFromBuffer( + mtlBuffer, + 0, + bytesPerRow, + bytesPerImage, + new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height }, + MTLTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } + ); } } -- 2.47.1 From 1e835aa56f28ea5afbe3b6e21768673909829e76 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 23:50:00 -0400 Subject: [PATCH 004/368] CopyTo Buffer --- src/Ryujinx.Graphics.Metal/Texture.cs | 42 ++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index c976dba6d..055236371 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -2,6 +2,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System; +using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -100,15 +101,42 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { - var samplerDescriptor = new MTLSamplerDescriptor(); - samplerDescriptor.MinFilter = linearFilter ? MTLSamplerMinMagFilter.Linear : MTLSamplerMinMagFilter.Nearest; - samplerDescriptor.MagFilter = linearFilter ? MTLSamplerMinMagFilter.Linear : MTLSamplerMinMagFilter.Nearest; - var samplerState = _device.NewSamplerState(samplerDescriptor); + throw new NotImplementedException(); } public void CopyTo(BufferRange range, int layer, int level, int stride) { - throw new NotImplementedException(); + MTLBlitCommandEncoder blitCommandEncoder; + + if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = _pipeline.BeginBlitPass(); + } + + ulong bytesPerRow = (ulong)Info.GetMipStride(level); + ulong bytesPerImage = 0; + if (MTLTexture.TextureType == MTLTextureType.Type3D) + { + bytesPerImage = bytesPerRow * (ulong)Info.Height; + } + + var handle = range.Handle; + MTLBuffer mtlBuffer = new(Unsafe.As(ref handle)); + + blitCommandEncoder.CopyFromTexture( + MTLTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin(), + new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height, depth = MTLTexture.Depth }, + mtlBuffer, + (ulong)range.Offset, + bytesPerRow, + bytesPerImage); } public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) @@ -163,7 +191,7 @@ namespace Ryujinx.Graphics.Metal 0, bytesPerRow, bytesPerImage, - new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height }, + new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height, depth = MTLTexture.Depth}, MTLTexture, (ulong)layer, (ulong)level, @@ -204,7 +232,7 @@ namespace Ryujinx.Graphics.Metal 0, bytesPerRow, bytesPerImage, - new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height }, + new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, MTLTexture, (ulong)layer, (ulong)level, -- 2.47.1 From 1d01fbf6b19686fe6248f4eac278ee8c784a8e9b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 23:56:33 -0400 Subject: [PATCH 005/368] CopyBuffer to Buffer --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index a0af3c33f..8ea0d1bc7 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -133,7 +133,26 @@ namespace Ryujinx.Graphics.Metal public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) { - throw new NotImplementedException(); + MTLBlitCommandEncoder blitCommandEncoder; + + if (CurrentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = BeginBlitPass(); + } + + MTLBuffer sourceBuffer = new(Unsafe.As(ref source)); + MTLBuffer destinationBuffer = new(Unsafe.As(ref destination)); + + blitCommandEncoder.CopyFromBuffer( + sourceBuffer, + (ulong)srcOffset, + destinationBuffer, + (ulong)dstOffset, + (ulong)size); } public void DispatchCompute(int groupsX, int groupsY, int groupsZ) -- 2.47.1 From 15ad03bc043312ef82b349902d235a26fdacd456 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 00:07:54 -0400 Subject: [PATCH 006/368] Texture usage --- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 055236371..e13dbf818 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLTextureDescriptor(); descriptor.PixelFormat = FormatTable.GetFormat(Info.Format); - // descriptor.Usage = + descriptor.Usage = MTLTextureUsage.ShaderRead | MTLTextureUsage.ShaderWrite | MTLTextureUsage.RenderTarget; descriptor.Width = (ulong)Width; descriptor.Height = (ulong)Height; descriptor.Depth = (ulong)Info.Depth; -- 2.47.1 From cc3c7901b6123deacaa8c4cb2478a93ed216d7f5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 00:14:21 -0400 Subject: [PATCH 007/368] Forgot depth --- src/Ryujinx.Graphics.Metal/Texture.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index e13dbf818..1c5fa86b9 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Metal public TextureCreateInfo Info => _info; public int Width => Info.Width; public int Height => Info.Height; + public int Depth => Info.Depth; public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) { @@ -30,6 +31,7 @@ namespace Ryujinx.Graphics.Metal descriptor.Usage = MTLTextureUsage.ShaderRead | MTLTextureUsage.ShaderWrite | MTLTextureUsage.RenderTarget; descriptor.Width = (ulong)Width; descriptor.Height = (ulong)Height; + descriptor.Depth = (ulong)Depth; descriptor.Depth = (ulong)Info.Depth; descriptor.SampleCount = (ulong)Info.Samples; descriptor.MipmapLevelCount = (ulong)Info.Levels; -- 2.47.1 From dc60b76748fa68aea2739e669384346c04e67a98 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 00:30:08 -0400 Subject: [PATCH 008/368] SetStencilTest --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 29 ++++++++++++++++++- .../RenderEncoderState.cs | 2 ++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 8ea0d1bc7..04a9bc26f 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -387,7 +387,34 @@ namespace Ryujinx.Graphics.Metal public void SetStencilTest(StencilTestDescriptor stencilTest) { - // TODO + var depthStencilDescriptor = new MTLDepthStencilDescriptor + { + BackFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = stencilTest.BackSFail.Convert(), + DepthFailureOperation = stencilTest.BackDpFail.Convert(), + DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), + StencilCompareFunction = stencilTest.BackFunc.Convert(), + ReadMask = (uint)stencilTest.BackFuncMask, + WriteMask = (uint)stencilTest.BackMask + }, + FrontFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = stencilTest.FrontSFail.Convert(), + DepthFailureOperation = stencilTest.FrontDpFail.Convert(), + DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), + StencilCompareFunction = stencilTest.FrontFunc.Convert(), + ReadMask = (uint)stencilTest.FrontFuncMask, + WriteMask = (uint)stencilTest.FrontMask + } + }; + + var depthStencilState = _device.NewDepthStencilState(depthStencilDescriptor); + + if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetDepthStencilState(depthStencilState); + } } public void SetStorageBuffers(ReadOnlySpan buffers) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 5f078ded6..8e0d7b093 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Metal public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.Clockwise; + public MTLDepthStencilState DepthStencilState = null; public RenderEncoderState(MTLRenderPipelineState renderPipelineState) { @@ -22,6 +23,7 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetRenderPipelineState(RenderPipelineState); renderCommandEncoder.SetCullMode(CullMode); renderCommandEncoder.SetFrontFacingWinding(Winding); + renderCommandEncoder.SetDepthStencilState(DepthStencilState); } } } -- 2.47.1 From 05002ae234dfc8180ff81d887eca734b4596bfc7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 00:46:13 -0400 Subject: [PATCH 009/368] SetDepthTest --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 49 ++++++++++--------- .../RenderEncoderState.cs | 44 +++++++++++++++-- 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 04a9bc26f..8ae89131a 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Metal var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); var error = new NSError(IntPtr.Zero); - _renderEncoderState = new(_device.NewRenderPipelineState(renderPipelineDescriptor, ref error)); + _renderEncoderState = new(_device.NewRenderPipelineState(renderPipelineDescriptor, ref error), _device); if (error != IntPtr.Zero) { Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); @@ -255,7 +255,14 @@ namespace Ryujinx.Graphics.Metal public void SetDepthTest(DepthTestDescriptor depthTest) { - throw new NotImplementedException(); + var depthStencilState = _renderEncoderState.UpdateDepthState( + depthTest.TestEnable ? MTLCompareFunction.Always : depthTest.Func.Convert(), + depthTest.WriteEnable); + + if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetDepthStencilState(depthStencilState); + } } public void SetFaceCulling(bool enable, Face face) @@ -387,29 +394,27 @@ namespace Ryujinx.Graphics.Metal public void SetStencilTest(StencilTestDescriptor stencilTest) { - var depthStencilDescriptor = new MTLDepthStencilDescriptor + var backFace = new MTLStencilDescriptor { - BackFaceStencil = new MTLStencilDescriptor - { - StencilFailureOperation = stencilTest.BackSFail.Convert(), - DepthFailureOperation = stencilTest.BackDpFail.Convert(), - DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), - StencilCompareFunction = stencilTest.BackFunc.Convert(), - ReadMask = (uint)stencilTest.BackFuncMask, - WriteMask = (uint)stencilTest.BackMask - }, - FrontFaceStencil = new MTLStencilDescriptor - { - StencilFailureOperation = stencilTest.FrontSFail.Convert(), - DepthFailureOperation = stencilTest.FrontDpFail.Convert(), - DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), - StencilCompareFunction = stencilTest.FrontFunc.Convert(), - ReadMask = (uint)stencilTest.FrontFuncMask, - WriteMask = (uint)stencilTest.FrontMask - } + StencilFailureOperation = stencilTest.BackSFail.Convert(), + DepthFailureOperation = stencilTest.BackDpFail.Convert(), + DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), + StencilCompareFunction = stencilTest.BackFunc.Convert(), + ReadMask = (uint)stencilTest.BackFuncMask, + WriteMask = (uint)stencilTest.BackMask }; - var depthStencilState = _device.NewDepthStencilState(depthStencilDescriptor); + var frontFace = new MTLStencilDescriptor + { + StencilFailureOperation = stencilTest.FrontSFail.Convert(), + DepthFailureOperation = stencilTest.FrontDpFail.Convert(), + DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), + StencilCompareFunction = stencilTest.FrontFunc.Convert(), + ReadMask = (uint)stencilTest.FrontFuncMask, + WriteMask = (uint)stencilTest.FrontMask + }; + + var depthStencilState = _renderEncoderState.UpdateStencilState(backFace, frontFace); if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) { diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 8e0d7b093..a8c719fe9 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -7,14 +7,24 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] struct RenderEncoderState { + private MTLDevice _device; + + private MTLDepthStencilState _depthStencilState = null; + + private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always; + private bool _depthWriteEnabled = false; + + private MTLStencilDescriptor _backFaceStencil = null; + private MTLStencilDescriptor _frontFaceStencil = null; + public MTLRenderPipelineState RenderPipelineState; public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.Clockwise; - public MTLDepthStencilState DepthStencilState = null; - public RenderEncoderState(MTLRenderPipelineState renderPipelineState) + public RenderEncoderState(MTLRenderPipelineState renderPipelineState, MTLDevice device) { + _device = device; RenderPipelineState = renderPipelineState; } @@ -23,7 +33,35 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetRenderPipelineState(RenderPipelineState); renderCommandEncoder.SetCullMode(CullMode); renderCommandEncoder.SetFrontFacingWinding(Winding); - renderCommandEncoder.SetDepthStencilState(DepthStencilState); + renderCommandEncoder.SetDepthStencilState(_depthStencilState); + } + + public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace) + { + _backFaceStencil = backFace; + _frontFaceStencil = frontFace; + + return _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + { + DepthCompareFunction = _depthCompareFunction, + DepthWriteEnabled = _depthWriteEnabled, + BackFaceStencil = _backFaceStencil, + FrontFaceStencil = _frontFaceStencil + }); + } + + public MTLDepthStencilState UpdateDepthState(MTLCompareFunction depthCompareFunction, bool depthWriteEnabled) + { + _depthCompareFunction = depthCompareFunction; + _depthWriteEnabled = depthWriteEnabled; + + return _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + { + DepthCompareFunction = _depthCompareFunction, + DepthWriteEnabled = _depthWriteEnabled, + BackFaceStencil = _backFaceStencil, + FrontFaceStencil = _frontFaceStencil + }); } } } -- 2.47.1 From e0ea464c40225d840be0924d6d5609c802f47518 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 01:18:51 -0400 Subject: [PATCH 010/368] BeginComputePass --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 8ae89131a..de6e6abd6 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -74,6 +74,17 @@ namespace Ryujinx.Graphics.Metal return blitCommandEncoder; } + public MTLComputeCommandEncoder BeginComputePass() + { + EndCurrentPass(); + + var descriptor = new MTLComputePassDescriptor { }; + var computeCommandEncoder = _commandBuffer.ComputeCommandEncoder(descriptor); + + _currentEncoder = computeCommandEncoder; + return computeCommandEncoder; + } + public void Present() { EndCurrentPass(); -- 2.47.1 From ff0362063af6d42bea76fceb728b34ada77615f8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 01:20:15 -0400 Subject: [PATCH 011/368] TODO --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index de6e6abd6..208419137 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Metal _device = device; _mtlCommandQueue = commandQueue; + // TODO: Recreate descriptor and encoder state as needed var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); var error = new NSError(IntPtr.Zero); _renderEncoderState = new(_device.NewRenderPipelineState(renderPipelineDescriptor, ref error), _device); @@ -38,6 +39,7 @@ namespace Ryujinx.Graphics.Metal { Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); } + // _commandBuffer = _mtlCommandQueue.CommandBuffer(); } -- 2.47.1 From a60ecea4c3907579ccd7ebb501466e7fa45270c9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 31 Jul 2023 17:44:01 -0400 Subject: [PATCH 012/368] Whitespace --- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 1c5fa86b9..1d73398d5 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -193,7 +193,7 @@ namespace Ryujinx.Graphics.Metal 0, bytesPerRow, bytesPerImage, - new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height, depth = MTLTexture.Depth}, + new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height, depth = MTLTexture.Depth }, MTLTexture, (ulong)layer, (ulong)level, -- 2.47.1 From 93f31bd08ad7036b0384a4c509bbdbe6d122036a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 1 Aug 2023 22:36:07 -0400 Subject: [PATCH 013/368] Look ma no crash --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 203 +++++++++--------- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 6 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 98 +++++---- .../Ryujinx.Graphics.Metal.csproj | 4 + .../Shaders/ColorBlitShaderSource.metal | 30 +++ src/Ryujinx.Graphics.Metal/Texture.cs | 9 +- src/Ryujinx.Graphics.Metal/Window.cs | 11 +- 7 files changed, 212 insertions(+), 149 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index a21271e1c..b093ff3e5 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -1,6 +1,6 @@ using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; using System; -using SharpMetal; namespace Ryujinx.Graphics.Metal { @@ -12,30 +12,30 @@ namespace Ryujinx.Graphics.Metal { _table = new MTLPixelFormat[Enum.GetNames(typeof(Format)).Length]; - Add(Format.R8Unorm, MTLPixelFormat.R8Unorm); - Add(Format.R8Snorm, MTLPixelFormat.R8Snorm); - Add(Format.R8Uint, MTLPixelFormat.R8Uint); - Add(Format.R8Sint, MTLPixelFormat.R8Sint); - Add(Format.R16Float, MTLPixelFormat.R16Float); - Add(Format.R16Unorm, MTLPixelFormat.R16Unorm); - Add(Format.R16Snorm, MTLPixelFormat.R16Snorm); - Add(Format.R16Uint, MTLPixelFormat.R16Uint); - Add(Format.R16Sint, MTLPixelFormat.R16Sint); - Add(Format.R32Float, MTLPixelFormat.R32Float); - Add(Format.R32Uint, MTLPixelFormat.R32Uint); - Add(Format.R32Sint, MTLPixelFormat.R32Sint); - Add(Format.R8G8Unorm, MTLPixelFormat.RG8Unorm); - Add(Format.R8G8Snorm, MTLPixelFormat.RG8Snorm); - Add(Format.R8G8Uint, MTLPixelFormat.RG8Uint); - Add(Format.R8G8Sint, MTLPixelFormat.RG8Sint); - Add(Format.R16G16Float, MTLPixelFormat.RG16Float); - Add(Format.R16G16Unorm, MTLPixelFormat.RG16Unorm); - Add(Format.R16G16Snorm, MTLPixelFormat.RG16Snorm); - Add(Format.R16G16Uint, MTLPixelFormat.RG16Uint); - Add(Format.R16G16Sint, MTLPixelFormat.RG16Sint); - Add(Format.R32G32Float, MTLPixelFormat.RG32Float); - Add(Format.R32G32Uint, MTLPixelFormat.RG32Uint); - Add(Format.R32G32Sint, MTLPixelFormat.RG32Sint); + Add(Format.R8Unorm, MTLPixelFormat.R8Unorm); + Add(Format.R8Snorm, MTLPixelFormat.R8Snorm); + Add(Format.R8Uint, MTLPixelFormat.R8Uint); + Add(Format.R8Sint, MTLPixelFormat.R8Sint); + Add(Format.R16Float, MTLPixelFormat.R16Float); + Add(Format.R16Unorm, MTLPixelFormat.R16Unorm); + Add(Format.R16Snorm, MTLPixelFormat.R16Snorm); + Add(Format.R16Uint, MTLPixelFormat.R16Uint); + Add(Format.R16Sint, MTLPixelFormat.R16Sint); + Add(Format.R32Float, MTLPixelFormat.R32Float); + Add(Format.R32Uint, MTLPixelFormat.R32Uint); + Add(Format.R32Sint, MTLPixelFormat.R32Sint); + Add(Format.R8G8Unorm, MTLPixelFormat.RG8Unorm); + Add(Format.R8G8Snorm, MTLPixelFormat.RG8Snorm); + Add(Format.R8G8Uint, MTLPixelFormat.RG8Uint); + Add(Format.R8G8Sint, MTLPixelFormat.RG8Sint); + Add(Format.R16G16Float, MTLPixelFormat.RG16Float); + Add(Format.R16G16Unorm, MTLPixelFormat.RG16Unorm); + Add(Format.R16G16Snorm, MTLPixelFormat.RG16Snorm); + Add(Format.R16G16Uint, MTLPixelFormat.RG16Uint); + Add(Format.R16G16Sint, MTLPixelFormat.RG16Sint); + Add(Format.R32G32Float, MTLPixelFormat.RG32Float); + Add(Format.R32G32Uint, MTLPixelFormat.RG32Uint); + Add(Format.R32G32Sint, MTLPixelFormat.RG32Sint); // Add(Format.R8G8B8Unorm, MTLPixelFormat.R8G8B8Unorm); // Add(Format.R8G8B8Snorm, MTLPixelFormat.R8G8B8Snorm); // Add(Format.R8G8B8Uint, MTLPixelFormat.R8G8B8Uint); @@ -48,53 +48,54 @@ namespace Ryujinx.Graphics.Metal // Add(Format.R32G32B32Float, MTLPixelFormat.R32G32B32Sfloat); // Add(Format.R32G32B32Uint, MTLPixelFormat.R32G32B32Uint); // Add(Format.R32G32B32Sint, MTLPixelFormat.R32G32B32Sint); - Add(Format.R8G8B8A8Unorm, MTLPixelFormat.RGBA8Unorm); - Add(Format.R8G8B8A8Snorm, MTLPixelFormat.RGBA8Snorm); - Add(Format.R8G8B8A8Uint, MTLPixelFormat.RGBA8Uint); - Add(Format.R8G8B8A8Sint, MTLPixelFormat.RGBA8Sint); - Add(Format.R16G16B16A16Float, MTLPixelFormat.RGBA16Float); - Add(Format.R16G16B16A16Unorm, MTLPixelFormat.RGBA16Unorm); - Add(Format.R16G16B16A16Snorm, MTLPixelFormat.RGBA16Snorm); - Add(Format.R16G16B16A16Uint, MTLPixelFormat.RGBA16Uint); - Add(Format.R16G16B16A16Sint, MTLPixelFormat.RGBA16Sint); - Add(Format.R32G32B32A32Float, MTLPixelFormat.RGBA32Float); - Add(Format.R32G32B32A32Uint, MTLPixelFormat.RGBA32Uint); - Add(Format.R32G32B32A32Sint, MTLPixelFormat.RGBA32Sint); - Add(Format.S8Uint, MTLPixelFormat.Stencil8); - Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm); - // Add(Format.S8UintD24Unorm, MTLPixelFormat.S8UintD24Unorm); - Add(Format.D32Float, MTLPixelFormat.Depth32Float); - Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24Unorm_Stencil8); - Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32Float_Stencil8); - Add(Format.R8G8B8A8Srgb, MTLPixelFormat.RGBA8Unorm_sRGB); + Add(Format.R8G8B8A8Unorm, MTLPixelFormat.RGBA8Unorm); + Add(Format.R8G8B8A8Snorm, MTLPixelFormat.RGBA8Snorm); + Add(Format.R8G8B8A8Uint, MTLPixelFormat.RGBA8Uint); + Add(Format.R8G8B8A8Sint, MTLPixelFormat.RGBA8Sint); + Add(Format.R16G16B16A16Float, MTLPixelFormat.RGBA16Float); + Add(Format.R16G16B16A16Unorm, MTLPixelFormat.RGBA16Unorm); + Add(Format.R16G16B16A16Snorm, MTLPixelFormat.RGBA16Snorm); + Add(Format.R16G16B16A16Uint, MTLPixelFormat.RGBA16Uint); + Add(Format.R16G16B16A16Sint, MTLPixelFormat.RGBA16Sint); + Add(Format.R32G32B32A32Float, MTLPixelFormat.RGBA32Float); + Add(Format.R32G32B32A32Uint, MTLPixelFormat.RGBA32Uint); + Add(Format.R32G32B32A32Sint, MTLPixelFormat.RGBA32Sint); + Add(Format.S8Uint, MTLPixelFormat.Stencil8); + Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm); + // Approximate + Add(Format.S8UintD24Unorm, MTLPixelFormat.BGRA8Unorm); + Add(Format.D32Float, MTLPixelFormat.Depth32Float); + Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24UnormStencil8); + Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8); + Add(Format.R8G8B8A8Srgb, MTLPixelFormat.RGBA8UnormsRGB); // Add(Format.R4G4Unorm, MTLPixelFormat.R4G4Unorm); // Add(Format.R4G4B4A4Unorm, MTLPixelFormat.R4G4B4A4Unorm); // Add(Format.R5G5B5X1Unorm, MTLPixelFormat.R5G5B5X1Unorm); // Add(Format.R5G5B5A1Unorm, MTLPixelFormat.R5G5B5A1Unorm); - Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm); - Add(Format.R10G10B10A2Unorm, MTLPixelFormat.RGB10A2Unorm); - Add(Format.R10G10B10A2Uint, MTLPixelFormat.RGB10A2Uint); - Add(Format.R11G11B10Float, MTLPixelFormat.RG11B10Float); - Add(Format.R9G9B9E5Float, MTLPixelFormat.RGB9E5Float); - Add(Format.Bc1RgbaUnorm, MTLPixelFormat.BC1_RGBA); - Add(Format.Bc2Unorm, MTLPixelFormat.BC2_RGBA); - Add(Format.Bc3Unorm, MTLPixelFormat.BC3_RGBA); - Add(Format.Bc1RgbaSrgb, MTLPixelFormat.BC1_RGBA_sRGB); - Add(Format.Bc2Srgb, MTLPixelFormat.BC2_RGBA_sRGB); - Add(Format.Bc3Srgb, MTLPixelFormat.BC3_RGBA_sRGB); - Add(Format.Bc4Unorm, MTLPixelFormat.BC4_RUnorm); - Add(Format.Bc4Snorm, MTLPixelFormat.BC4_RSnorm); - Add(Format.Bc5Unorm, MTLPixelFormat.BC5_RGUnorm); - Add(Format.Bc5Snorm, MTLPixelFormat.BC5_RGSnorm); - Add(Format.Bc7Unorm, MTLPixelFormat.BC7_RGBAUnorm); - Add(Format.Bc7Srgb, MTLPixelFormat.BC7_RGBAUnorm_sRGB); - Add(Format.Bc6HSfloat, MTLPixelFormat.BC6H_RGBFloat); - Add(Format.Bc6HUfloat, MTLPixelFormat.BC6H_RGBUfloat); - Add(Format.Etc2RgbUnorm, MTLPixelFormat.ETC2_RGB8); - Add(Format.Etc2RgbaUnorm, MTLPixelFormat.ETC2_RGB8A1); + Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm); + Add(Format.R10G10B10A2Unorm, MTLPixelFormat.RGB10A2Unorm); + Add(Format.R10G10B10A2Uint, MTLPixelFormat.RGB10A2Uint); + Add(Format.R11G11B10Float, MTLPixelFormat.RG11B10Float); + Add(Format.R9G9B9E5Float, MTLPixelFormat.RGB9E5Float); + Add(Format.Bc1RgbaUnorm, MTLPixelFormat.BC1RGBA); + Add(Format.Bc2Unorm, MTLPixelFormat.BC2RGBA); + Add(Format.Bc3Unorm, MTLPixelFormat.BC3RGBA); + Add(Format.Bc1RgbaSrgb, MTLPixelFormat.BC1RGBAsRGB); + Add(Format.Bc2Srgb, MTLPixelFormat.BC2RGBAsRGB); + Add(Format.Bc3Srgb, MTLPixelFormat.BC3RGBAsRGB); + Add(Format.Bc4Unorm, MTLPixelFormat.BC4RUnorm); + Add(Format.Bc4Snorm, MTLPixelFormat.BC4RSnorm); + Add(Format.Bc5Unorm, MTLPixelFormat.BC5RGUnorm); + Add(Format.Bc5Snorm, MTLPixelFormat.BC5RGSnorm); + Add(Format.Bc7Unorm, MTLPixelFormat.BC7RGBAUnorm); + Add(Format.Bc7Srgb, MTLPixelFormat.BC7RGBAUnormsRGB); + Add(Format.Bc6HSfloat, MTLPixelFormat.BC6HRGBFloat); + Add(Format.Bc6HUfloat, MTLPixelFormat.BC6HRGBUfloat); + Add(Format.Etc2RgbUnorm, MTLPixelFormat.ETC2RGB8); + Add(Format.Etc2RgbaUnorm, MTLPixelFormat.ETC2RGB8A1); // Add(Format.Etc2RgbPtaUnorm, MTLPixelFormat.Etc2RgbPtaUnorm); - Add(Format.Etc2RgbSrgb, MTLPixelFormat.ETC2_RGB8_sRGB); - Add(Format.Etc2RgbaSrgb, MTLPixelFormat.ETC2_RGB8A1_sRGB); + Add(Format.Etc2RgbSrgb, MTLPixelFormat.ETC2RGB8sRGB); + Add(Format.Etc2RgbaSrgb, MTLPixelFormat.ETC2RGB8A1sRGB); // Add(Format.Etc2RgbPtaSrgb, MTLPixelFormat.Etc2RgbPtaSrgb); // Add(Format.R8Uscaled, MTLPixelFormat.R8Uscaled); // Add(Format.R8Sscaled, MTLPixelFormat.R8Sscaled); @@ -124,39 +125,39 @@ namespace Ryujinx.Graphics.Metal // Add(Format.R10G10B10A2Sint, MTLPixelFormat.A2B10G10R10SintPack32); // Add(Format.R10G10B10A2Uscaled, MTLPixelFormat.A2B10G10R10UscaledPack32); // Add(Format.R10G10B10A2Sscaled, MTLPixelFormat.A2B10G10R10SscaledPack32); - Add(Format.Astc4x4Unorm, MTLPixelFormat.ASTC_4x4_LDR); - Add(Format.Astc5x4Unorm, MTLPixelFormat.ASTC_5x4_LDR); - Add(Format.Astc5x5Unorm, MTLPixelFormat.ASTC_5x5_LDR); - Add(Format.Astc6x5Unorm, MTLPixelFormat.ASTC_6x5_LDR); - Add(Format.Astc6x6Unorm, MTLPixelFormat.ASTC_6x6_LDR); - Add(Format.Astc8x5Unorm, MTLPixelFormat.ASTC_8x5_LDR); - Add(Format.Astc8x6Unorm, MTLPixelFormat.ASTC_8x6_LDR); - Add(Format.Astc8x8Unorm, MTLPixelFormat.ASTC_8x8_LDR); - Add(Format.Astc10x5Unorm, MTLPixelFormat.ASTC_10x5_LDR); - Add(Format.Astc10x6Unorm, MTLPixelFormat.ASTC_10x6_LDR); - Add(Format.Astc10x8Unorm, MTLPixelFormat.ASTC_10x8_LDR); - Add(Format.Astc10x10Unorm, MTLPixelFormat.ASTC_10x10_LDR); - Add(Format.Astc12x10Unorm, MTLPixelFormat.ASTC_12x10_LDR); - Add(Format.Astc12x12Unorm, MTLPixelFormat.ASTC_12x12_LDR); - Add(Format.Astc4x4Srgb, MTLPixelFormat.ASTC_4x4_sRGB); - Add(Format.Astc5x4Srgb, MTLPixelFormat.ASTC_5x4_sRGB); - Add(Format.Astc5x5Srgb, MTLPixelFormat.ASTC_5x5_sRGB); - Add(Format.Astc6x5Srgb, MTLPixelFormat.ASTC_6x5_sRGB); - Add(Format.Astc6x6Srgb, MTLPixelFormat.ASTC_6x6_sRGB); - Add(Format.Astc8x5Srgb, MTLPixelFormat.ASTC_8x5_sRGB); - Add(Format.Astc8x6Srgb, MTLPixelFormat.ASTC_8x6_sRGB); - Add(Format.Astc8x8Srgb, MTLPixelFormat.ASTC_8x8_sRGB); - Add(Format.Astc10x5Srgb, MTLPixelFormat.ASTC_10x5_sRGB); - Add(Format.Astc10x6Srgb, MTLPixelFormat.ASTC_10x6_sRGB); - Add(Format.Astc10x8Srgb, MTLPixelFormat.ASTC_10x8_sRGB); - Add(Format.Astc10x10Srgb, MTLPixelFormat.ASTC_10x10_sRGB); - Add(Format.Astc12x10Srgb, MTLPixelFormat.ASTC_12x10_sRGB); - Add(Format.Astc12x12Srgb, MTLPixelFormat.ASTC_12x12_sRGB); - Add(Format.B5G6R5Unorm, MTLPixelFormat.B5G6R5Unorm); - Add(Format.B5G5R5A1Unorm, MTLPixelFormat.BGR5A1Unorm); - Add(Format.A1B5G5R5Unorm, MTLPixelFormat.A1BGR5Unorm); - Add(Format.B8G8R8A8Unorm, MTLPixelFormat.BGRA8Unorm); - Add(Format.B8G8R8A8Srgb, MTLPixelFormat.BGRA8Unorm_sRGB); + Add(Format.Astc4x4Unorm, MTLPixelFormat.ASTC4x4HDR); + Add(Format.Astc5x4Unorm, MTLPixelFormat.ASTC5x4LDR); + Add(Format.Astc5x5Unorm, MTLPixelFormat.ASTC5x5LDR); + Add(Format.Astc6x5Unorm, MTLPixelFormat.ASTC6x5LDR); + Add(Format.Astc6x6Unorm, MTLPixelFormat.ASTC6x6LDR); + Add(Format.Astc8x5Unorm, MTLPixelFormat.ASTC8x5LDR); + Add(Format.Astc8x6Unorm, MTLPixelFormat.ASTC8x6LDR); + Add(Format.Astc8x8Unorm, MTLPixelFormat.ASTC8x8LDR); + Add(Format.Astc10x5Unorm, MTLPixelFormat.ASTC10x5LDR); + Add(Format.Astc10x6Unorm, MTLPixelFormat.ASTC10x6LDR); + Add(Format.Astc10x8Unorm, MTLPixelFormat.ASTC10x8LDR); + Add(Format.Astc10x10Unorm, MTLPixelFormat.ASTC10x10LDR); + Add(Format.Astc12x10Unorm, MTLPixelFormat.ASTC12x10LDR); + Add(Format.Astc12x12Unorm, MTLPixelFormat.ASTC12x12LDR); + Add(Format.Astc4x4Srgb, MTLPixelFormat.ASTC4x4sRGB); + Add(Format.Astc5x4Srgb, MTLPixelFormat.ASTC5x4sRGB); + Add(Format.Astc5x5Srgb, MTLPixelFormat.ASTC5x5sRGB); + Add(Format.Astc6x5Srgb, MTLPixelFormat.ASTC6x5sRGB); + Add(Format.Astc6x6Srgb, MTLPixelFormat.ASTC6x6sRGB); + Add(Format.Astc8x5Srgb, MTLPixelFormat.ASTC8x5sRGB); + Add(Format.Astc8x6Srgb, MTLPixelFormat.ASTC8x6sRGB); + Add(Format.Astc8x8Srgb, MTLPixelFormat.ASTC8x8sRGB); + Add(Format.Astc10x5Srgb, MTLPixelFormat.ASTC10x5sRGB); + Add(Format.Astc10x6Srgb, MTLPixelFormat.ASTC10x6sRGB); + Add(Format.Astc10x8Srgb, MTLPixelFormat.ASTC10x8sRGB); + Add(Format.Astc10x10Srgb, MTLPixelFormat.ASTC10x10sRGB); + Add(Format.Astc12x10Srgb, MTLPixelFormat.ASTC12x10sRGB); + Add(Format.Astc12x12Srgb, MTLPixelFormat.ASTC12x12sRGB); + Add(Format.B5G6R5Unorm, MTLPixelFormat.B5G6R5Unorm); + Add(Format.B5G5R5A1Unorm, MTLPixelFormat.BGR5A1Unorm); + Add(Format.A1B5G5R5Unorm, MTLPixelFormat.A1BGR5Unorm); + Add(Format.B8G8R8A8Unorm, MTLPixelFormat.BGRA8Unorm); + Add(Format.B8G8R8A8Srgb, MTLPixelFormat.BGRA8UnormsRGB); } private static void Add(Format format, MTLPixelFormat mtlFormat) @@ -169,4 +170,4 @@ namespace Ryujinx.Graphics.Metal return _table[(int)format]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index dc6b3d05a..05f9a7911 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Foundation; @@ -106,7 +107,7 @@ namespace Ryujinx.Graphics.Metal public void CreateSync(ulong id, bool strict) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DeleteBuffer(BufferHandle buffer) @@ -175,7 +176,8 @@ namespace Ryujinx.Graphics.Metal public ulong GetCurrentSync() { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + return 0; } public HardwareInfo GetHardwareInfo() diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 208419137..77a0ee1a6 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; @@ -12,6 +13,8 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public class Pipeline : IPipeline, IDisposable { + private const string ShaderSourcePath = "Ryujinx.Graphics.Metal/Shaders"; + private readonly MTLDevice _device; private readonly MTLCommandQueue _mtlCommandQueue; @@ -31,9 +34,23 @@ namespace Ryujinx.Graphics.Metal _device = device; _mtlCommandQueue = commandQueue; + var error = new NSError(IntPtr.Zero); + + var shaderSource = EmbeddedResources.ReadAllText(string.Join('/', ShaderSourcePath, "ColorBlitShaderSource.metal")); + var library = _device.NewLibrary(StringHelper.NSString(shaderSource), new(IntPtr.Zero), ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Library: {StringHelper.String(error.LocalizedDescription)}"); + } + + var vertexFunction = library.NewFunction(StringHelper.NSString("vertexMain")); + var fragmentFunction = library.NewFunction(StringHelper.NSString("fragmentMain")); + // TODO: Recreate descriptor and encoder state as needed var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - var error = new NSError(IntPtr.Zero); + renderPipelineDescriptor.VertexFunction = vertexFunction; + renderPipelineDescriptor.FragmentFunction = fragmentFunction; + _renderEncoderState = new(_device.NewRenderPipelineState(renderPipelineDescriptor, ref error), _device); if (error != IntPtr.Zero) { @@ -57,7 +74,7 @@ namespace Ryujinx.Graphics.Metal { EndCurrentPass(); - var descriptor = new MTLRenderPassDescriptor { }; + var descriptor = new MTLRenderPassDescriptor(); var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder); @@ -69,7 +86,7 @@ namespace Ryujinx.Graphics.Metal { EndCurrentPass(); - var descriptor = new MTLBlitPassDescriptor { }; + var descriptor = new MTLBlitPassDescriptor(); var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); _currentEncoder = blitCommandEncoder; @@ -80,7 +97,7 @@ namespace Ryujinx.Graphics.Metal { EndCurrentPass(); - var descriptor = new MTLComputePassDescriptor { }; + var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _commandBuffer.ComputeCommandEncoder(descriptor); _currentEncoder = computeCommandEncoder; @@ -93,14 +110,14 @@ namespace Ryujinx.Graphics.Metal // TODO: Give command buffer a valid MTLDrawable // _commandBuffer.PresentDrawable(); - _commandBuffer.Commit(); + // _commandBuffer.Commit(); _commandBuffer = _mtlCommandQueue.CommandBuffer(); } public void Barrier() { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) @@ -130,18 +147,18 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void CommandBufferBarrier() { - // TODO: Only required for MTLHeap or untracked resources + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) @@ -170,7 +187,7 @@ namespace Ryujinx.Graphics.Metal public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) @@ -213,57 +230,57 @@ namespace Ryujinx.Graphics.Metal public void DrawIndexedIndirect(BufferRange indirectBuffer) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndirect(BufferRange indirectBuffer) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetAlphaTest(bool enable, float reference, CompareOp op) { - // Metal does not support alpha test. + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetBlendState(AdvancedBlendDescriptor blend) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetBlendState(int index, BlendDescriptor blend) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetDepthClamp(bool clamp) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetDepthMode(DepthMode mode) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetDepthTest(DepthTestDescriptor depthTest) @@ -315,37 +332,40 @@ namespace Ryujinx.Graphics.Metal public void SetImage(int binding, ITexture texture, Format imageFormat) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetLineParameters(float width, bool smooth) { // Not supported in Metal + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetLogicOpState(bool enable, LogicalOp op) { // Not supported in Metal + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetMultisampleState(MultisampleDescriptor multisample) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) { // Not supported in Metal + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetPrimitiveRestart(bool enable, int index) @@ -354,6 +374,7 @@ namespace Ryujinx.Graphics.Metal // https://github.com/gpuweb/gpuweb/issues/1220#issuecomment-732483263 // https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515520-drawindexedprimitives // https://stackoverflow.com/questions/70813665/how-to-render-multiple-trianglestrips-using-metal + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetPrimitiveTopology(PrimitiveTopology topology) @@ -363,22 +384,22 @@ namespace Ryujinx.Graphics.Metal public void SetProgram(IProgram program) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetRasterizerDiscard(bool discard) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public unsafe void SetScissors(ReadOnlySpan> regions) @@ -437,32 +458,32 @@ namespace Ryujinx.Graphics.Metal public void SetStorageBuffers(ReadOnlySpan buffers) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetUniformBuffers(ReadOnlySpan buffers) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetUserClipDistance(int index, bool enableClip) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetVertexAttribs(ReadOnlySpan vertexAttribs) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public unsafe void SetViewports(ReadOnlySpan viewports) @@ -493,12 +514,12 @@ namespace Ryujinx.Graphics.Metal public void TextureBarrier() { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void TextureBarrierTiled() { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) @@ -521,16 +542,19 @@ namespace Ryujinx.Graphics.Metal public void BeginTransformFeedback(PrimitiveTopology topology) { // Metal does not support Transform Feedback + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void EndTransformFeedback() { // Metal does not support Transform Feedback + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) { // Metal does not support Transform Feedback + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void Dispose() diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 6e8b00183..638e1db67 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -9,6 +9,10 @@ + + + + diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal new file mode 100644 index 000000000..3c2c8aa5f --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal @@ -0,0 +1,30 @@ +#include + +using namespace metal; + +struct TexCoordIn { + float4 tex_coord_in_data; +}; + +vertex float4 vertexMain(uint vertexID [[vertex_id]], + constant TexCoordIn& tex_coord_in [[buffer(1)]]) { + int low = vertexID & 1; + int high = vertexID >> 1; + float2 tex_coord; + tex_coord.x = tex_coord_in.tex_coord_in_data[low]; + tex_coord.y = tex_coord_in.tex_coord_in_data[2 + high]; + + float4 position; + position.x = (float(low) - 0.5) * 2.0; + position.y = (float(high) - 0.5) * 2.0; + position.z = 0.0; + position.w = 1.0; + + return position; +} + +fragment float4 fragmentMain(float2 tex_coord [[stage_in]], + texture2d tex [[texture(0)]]) { + float4 color = tex.sample(metal::address::clamp_to_edge, tex_coord); + return color; +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 1d73398d5..205546eb2 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; @@ -158,7 +159,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetData(SpanOrArray data, int layer, int level) @@ -245,17 +246,17 @@ namespace Ryujinx.Graphics.Metal public void SetStorage(BufferRange buffer) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void Release() { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void Dispose() { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 31c534958..3a34293a0 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using System; using System.Runtime.Versioning; @@ -24,27 +25,27 @@ namespace Ryujinx.Graphics.Metal public void SetSize(int width, int height) { - // Not needed as we can get the size from the surface. + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void ChangeVSyncMode(bool vsyncEnabled) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetAntiAliasing(AntiAliasing antialiasing) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetScalingFilter(ScalingFilter type) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetScalingFilterLevel(float level) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void Dispose() -- 2.47.1 From ed445e001af881802c06184c068ba21551e16696 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 15:57:57 -0400 Subject: [PATCH 014/368] SetData --- src/Ryujinx.Graphics.Metal/Texture.cs | 56 ++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 205546eb2..2a1e0a48c 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -157,9 +157,61 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void SetData(SpanOrArray data) + // TODO: Handle array formats + public unsafe void SetData(SpanOrArray data) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + MTLBlitCommandEncoder blitCommandEncoder; + + if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = _pipeline.BeginBlitPass(); + } + + var dataSpan = data.Span; + var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); + var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); + dataSpan.CopyTo(bufferSpan); + + int width = Info.Width; + int height = Info.Height; + int depth = Info.Depth; + int levels = Info.GetLevelsClamped(); + + int offset = 0; + + for (int level = 0; level < levels; level++) + { + int mipSize = Info.GetMipSize(level); + + int endOffset = offset + mipSize; + + if ((uint)endOffset > (uint)dataSpan.Length) + { + return; + } + + blitCommandEncoder.CopyFromBuffer( + mtlBuffer, + (ulong)offset, + (ulong)Info.GetMipStride(level), + (ulong)mipSize, + new MTLSize { width = (ulong)width, height = (ulong)height, depth = (ulong)depth }, + MTLTexture, + 0, + (ulong)level, + new MTLOrigin() + ); + + offset += mipSize; + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + depth = Math.Max(1, depth >> 1); + } } public void SetData(SpanOrArray data, int layer, int level) -- 2.47.1 From 6a115becefeb1ab5c71ee35b6e87adc9dc17ce6e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 19:56:59 -0400 Subject: [PATCH 015/368] Seizure my beloved is working --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 34 ++++++++++--- src/Ryujinx.Graphics.Metal/Pipeline.cs | 51 ++++++++++++++++--- .../RenderEncoderState.cs | 10 ++-- .../Shaders/ColorBlitShaderSource.metal | 44 +++++++++------- src/Ryujinx.Graphics.Metal/Texture.cs | 9 +++- 5 files changed, 109 insertions(+), 39 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 05f9a7911..434a73747 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Foundation; using SharpMetal.Metal; +using SharpMetal.QuartzCore; using System; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -14,25 +15,43 @@ namespace Ryujinx.Graphics.Metal public sealed class MetalRenderer : IRenderer { private readonly MTLDevice _device; - private readonly Window _window; - private readonly Pipeline _pipeline; private readonly MTLCommandQueue _queue; + private readonly Func _getMetalLayer; + + private Pipeline _pipeline; + private Window _window; public event EventHandler ScreenCaptured; public bool PreferThreading => true; public IPipeline Pipeline => _pipeline; public IWindow Window => _window; - public MetalRenderer() + public MetalRenderer(Func metalLayer) { _device = MTLDevice.CreateSystemDefaultDevice(); _queue = _device.NewCommandQueue(); - _pipeline = new Pipeline(_device, _queue); - _window = new Window(this); + _getMetalLayer = metalLayer; } public void Initialize(GraphicsDebugLevel logLevel) { + var layer = _getMetalLayer(); + layer.Device = _device; + + var captureDescriptor = new MTLCaptureDescriptor(); + captureDescriptor.CaptureObject = _queue; + captureDescriptor.Destination = MTLCaptureDestination.GPUTraceDocument; + captureDescriptor.OutputURL = NSURL.FileURLWithPath(StringHelper.NSString("/Users/isaacmarovitz/Desktop/Trace.gputrace")); + var captureError = new NSError(IntPtr.Zero); + MTLCaptureManager.SharedCaptureManager().StartCapture(captureDescriptor, ref captureError); + if (captureError != IntPtr.Zero) + { + Console.Write($"Failed to start capture! {StringHelper.String(captureError.LocalizedDescription)}"); + + } + + _window = new Window(this, layer); + _pipeline = new Pipeline(_device, _queue, layer); } public void BackgroundContextAction(Action action, bool alwaysBackground = false) @@ -96,7 +115,10 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateTexture(TextureCreateInfo info) { - return new Texture(_device, _pipeline, info); + var texture = new Texture(_device, _pipeline, info); + Logger.Warning?.Print(LogClass.Gpu, "Texture created!"); + + return texture; } public bool PrepareHostMapping(IntPtr address, ulong size) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 77a0ee1a6..4c2f5e300 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using SharpMetal.Foundation; using SharpMetal.Metal; +using SharpMetal.QuartzCore; using System; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -28,8 +29,10 @@ namespace Ryujinx.Graphics.Metal private MTLBuffer _indexBuffer; private MTLIndexType _indexType; private ulong _indexBufferOffset; + private MTLClearColor _clearColor = new() { alpha = 1.0f }; + private int frameCount = 0; - public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) + public Pipeline(MTLDevice device, MTLCommandQueue commandQueue, CAMetalLayer metalLayer) { _device = device; _mtlCommandQueue = commandQueue; @@ -49,13 +52,17 @@ namespace Ryujinx.Graphics.Metal // TODO: Recreate descriptor and encoder state as needed var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); renderPipelineDescriptor.VertexFunction = vertexFunction; - renderPipelineDescriptor.FragmentFunction = fragmentFunction; + // renderPipelineDescriptor.FragmentFunction = fragmentFunction; + // TODO: This should not be hardcoded, but a bug in SharpMetal prevents me from doing this correctly + renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; - _renderEncoderState = new(_device.NewRenderPipelineState(renderPipelineDescriptor, ref error), _device); + var renderPipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); if (error != IntPtr.Zero) { Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); } + + _renderEncoderState = new RenderEncoderState(renderPipelineState, _device); // _commandBuffer = _mtlCommandQueue.CommandBuffer(); @@ -68,6 +75,7 @@ namespace Ryujinx.Graphics.Metal _currentEncoder.EndEncoding(); _currentEncoder = null; } + Logger.Warning?.Print(LogClass.Gpu, "Current pass ended"); } public MTLRenderCommandEncoder BeginRenderPass() @@ -77,6 +85,7 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLRenderPassDescriptor(); var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder); + Logger.Warning?.Print(LogClass.Gpu, "Began render pass"); _currentEncoder = renderCommandEncoder; return renderCommandEncoder; @@ -88,6 +97,7 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLBlitPassDescriptor(); var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); + Logger.Warning?.Print(LogClass.Gpu, "Began blit pass"); _currentEncoder = blitCommandEncoder; return blitCommandEncoder; @@ -99,18 +109,43 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _commandBuffer.ComputeCommandEncoder(descriptor); + Logger.Warning?.Print(LogClass.Gpu, "Began compute pass"); _currentEncoder = computeCommandEncoder; return computeCommandEncoder; } - public void Present() + public void Present(CAMetalDrawable drawable) { EndCurrentPass(); - // TODO: Give command buffer a valid MTLDrawable - // _commandBuffer.PresentDrawable(); - // _commandBuffer.Commit(); + var descriptor = new MTLRenderPassDescriptor(); + descriptor.ColorAttachments.Object(0).Texture = drawable.Texture; + descriptor.ColorAttachments.Object(0).LoadAction = MTLLoadAction.Clear; + descriptor.ColorAttachments.Object(0).ClearColor = _clearColor; + + var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); + Logger.Warning?.Print(LogClass.Gpu, "Began present"); + _renderEncoderState.SetEncoderState(renderCommandEncoder); + + // Barry goes here + // renderCommandEncoder.SetFragmentTexture(_renderTarget, 0); + + renderCommandEncoder.DrawPrimitives(MTLPrimitiveType.Triangle, 0, 6); + renderCommandEncoder.EndEncoding(); + + _commandBuffer.PresentDrawable(drawable); + _commandBuffer.Commit(); + + Logger.Warning?.Print(LogClass.Gpu, "Presented"); + + frameCount++; + + if (frameCount >= 5) + { + MTLCaptureManager.SharedCaptureManager().StopCapture(); + Logger.Warning?.Print(LogClass.Gpu, "Trace ended!"); + } _commandBuffer = _mtlCommandQueue.CommandBuffer(); } @@ -147,7 +182,7 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _clearColor = new MTLClearColor { red = color.Red, green = color.Green, blue = color.Blue, alpha = color.Alpha}; } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index a8c719fe9..db946d8b8 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -17,23 +17,23 @@ namespace Ryujinx.Graphics.Metal private MTLStencilDescriptor _backFaceStencil = null; private MTLStencilDescriptor _frontFaceStencil = null; - public MTLRenderPipelineState RenderPipelineState; + public MTLRenderPipelineState CopyPipeline; public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.Clockwise; - public RenderEncoderState(MTLRenderPipelineState renderPipelineState, MTLDevice device) + public RenderEncoderState(MTLRenderPipelineState copyPipeline, MTLDevice device) { _device = device; - RenderPipelineState = renderPipelineState; + CopyPipeline = copyPipeline; } public void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder) { - renderCommandEncoder.SetRenderPipelineState(RenderPipelineState); + renderCommandEncoder.SetRenderPipelineState(CopyPipeline); renderCommandEncoder.SetCullMode(CullMode); renderCommandEncoder.SetFrontFacingWinding(Winding); - renderCommandEncoder.SetDepthStencilState(_depthStencilState); + // renderCommandEncoder.SetDepthStencilState(_depthStencilState); } public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace) diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal index 3c2c8aa5f..f21f219f0 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal @@ -2,29 +2,35 @@ using namespace metal; -struct TexCoordIn { - float4 tex_coord_in_data; +constant float2 quadVertices[] = { + float2(-1, -1), + float2(-1, 1), + float2( 1, 1), + float2(-1, -1), + float2( 1, 1), + float2( 1, -1) }; -vertex float4 vertexMain(uint vertexID [[vertex_id]], - constant TexCoordIn& tex_coord_in [[buffer(1)]]) { - int low = vertexID & 1; - int high = vertexID >> 1; - float2 tex_coord; - tex_coord.x = tex_coord_in.tex_coord_in_data[low]; - tex_coord.y = tex_coord_in.tex_coord_in_data[2 + high]; +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; - float4 position; - position.x = (float(low) - 0.5) * 2.0; - position.y = (float(high) - 0.5) * 2.0; - position.z = 0.0; - position.w = 1.0; +vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) { + float2 position = quadVertices[vid]; - return position; + CopyVertexOut out; + + out.position = float4(position, 0, 1); + out.uv = position * 0.5f + 0.5f; + + return out; } -fragment float4 fragmentMain(float2 tex_coord [[stage_in]], - texture2d tex [[texture(0)]]) { - float4 color = tex.sample(metal::address::clamp_to_edge, tex_coord); - return color; +fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], + texture2d tex) { + constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); + + float3 color = tex.sample(sam, in.uv).xyz; + return float4(color, 1.0f); } diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 2a1e0a48c..d8607a618 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -9,7 +9,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class Texture : ITexture, IDisposable + public class Texture : ITexture, IDisposable { private readonly TextureCreateInfo _info; private readonly Pipeline _pipeline; @@ -23,6 +23,7 @@ namespace Ryujinx.Graphics.Metal public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) { + Logger.Warning?.Print(LogClass.Gpu, "Texture init"); _device = device; _pipeline = pipeline; _info = info; @@ -50,6 +51,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { + Logger.Warning?.Print(LogClass.Gpu, "Copy to"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -77,6 +79,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { + Logger.Warning?.Print(LogClass.Gpu, "Copy to"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -109,6 +112,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(BufferRange range, int layer, int level, int stride) { + Logger.Warning?.Print(LogClass.Gpu, "Copy to"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -160,6 +164,7 @@ namespace Ryujinx.Graphics.Metal // TODO: Handle array formats public unsafe void SetData(SpanOrArray data) { + Logger.Warning?.Print(LogClass.Gpu, "Set data"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -216,6 +221,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data, int layer, int level) { + Logger.Warning?.Print(LogClass.Gpu, "Set data"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -257,6 +263,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { + Logger.Warning?.Print(LogClass.Gpu, "Set data"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) -- 2.47.1 From 8bf33b3098f3561aba2342a09458668fbfb1ed24 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 20:32:59 -0400 Subject: [PATCH 016/368] Barry is here mashallah --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 7 +++---- .../Shaders/ColorBlitShaderSource.metal | 1 + src/Ryujinx.Graphics.Metal/Window.cs | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 4c2f5e300..963863f38 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Metal // TODO: Recreate descriptor and encoder state as needed var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); renderPipelineDescriptor.VertexFunction = vertexFunction; - // renderPipelineDescriptor.FragmentFunction = fragmentFunction; + renderPipelineDescriptor.FragmentFunction = fragmentFunction; // TODO: This should not be hardcoded, but a bug in SharpMetal prevents me from doing this correctly renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; @@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Metal return computeCommandEncoder; } - public void Present(CAMetalDrawable drawable) + public void Present(CAMetalDrawable drawable, Texture texture) { EndCurrentPass(); @@ -128,8 +128,7 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Began present"); _renderEncoderState.SetEncoderState(renderCommandEncoder); - // Barry goes here - // renderCommandEncoder.SetFragmentTexture(_renderTarget, 0); + renderCommandEncoder.SetFragmentTexture(texture.MTLTexture, 0); renderCommandEncoder.DrawPrimitives(MTLPrimitiveType.Triangle, 0, 6); renderCommandEncoder.EndEncoding(); diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal index f21f219f0..35b314460 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal @@ -22,6 +22,7 @@ vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) { CopyVertexOut out; out.position = float4(position, 0, 1); + out.position.y = -out.position.y; out.uv = position * 0.5f + 0.5f; return out; diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 3a34293a0..12ff35e7f 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -1,5 +1,8 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using SharpMetal.ObjectiveCCore; +using SharpMetal.QuartzCore; using System; using System.Runtime.Versioning; @@ -9,17 +12,20 @@ namespace Ryujinx.Graphics.Metal public class Window : IWindow, IDisposable { private readonly MetalRenderer _renderer; + private readonly CAMetalLayer _metalLayer; - public Window(MetalRenderer renderer) + public Window(MetalRenderer renderer, CAMetalLayer metalLayer) { _renderer = renderer; + _metalLayer = metalLayer; } public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { - if (_renderer.Pipeline is Pipeline pipeline) + if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) { - pipeline.Present(); + var drawable = new CAMetalDrawable(ObjectiveC.IntPtr_objc_msgSend(_metalLayer, "nextDrawable")); + pipeline.Present(drawable, tex); } } -- 2.47.1 From 671aff68a68092df546001de9d07cbc675a3f7d8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 21:18:28 -0400 Subject: [PATCH 017/368] Fix RGB Seizure --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 7 ++++++- .../Shaders/ColorBlitShaderSource.metal | 3 +-- src/Ryujinx.Graphics.Metal/Window.cs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 963863f38..47d8f52ff 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Metal private MTLBuffer _indexBuffer; private MTLIndexType _indexType; private ulong _indexBufferOffset; - private MTLClearColor _clearColor = new() { alpha = 1.0f }; + private MTLClearColor _clearColor; private int frameCount = 0; public Pipeline(MTLDevice device, MTLCommandQueue commandQueue, CAMetalLayer metalLayer) @@ -54,7 +54,12 @@ namespace Ryujinx.Graphics.Metal renderPipelineDescriptor.VertexFunction = vertexFunction; renderPipelineDescriptor.FragmentFunction = fragmentFunction; // TODO: This should not be hardcoded, but a bug in SharpMetal prevents me from doing this correctly + renderPipelineDescriptor.ColorAttachments.Object(0).SetBlendingEnabled(true); renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; + renderPipelineDescriptor.ColorAttachments.Object(0).SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; var renderPipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); if (error != IntPtr.Zero) diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal index 35b314460..a1630e7b8 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal @@ -32,6 +32,5 @@ fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], texture2d tex) { constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); - float3 color = tex.sample(sam, in.uv).xyz; - return float4(color, 1.0f); + return tex.sample(sam, in.uv).xyzw; } diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 12ff35e7f..563f888af 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; using SharpMetal.ObjectiveCCore; using SharpMetal.QuartzCore; using System; @@ -20,6 +19,7 @@ namespace Ryujinx.Graphics.Metal _metalLayer = metalLayer; } + // TODO: Handle ImageCrop public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) -- 2.47.1 From 179482e9cb3378363226cf4e0190659645b4a07d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 21:32:41 -0400 Subject: [PATCH 018/368] Check if packed depth is supported --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 14 +++++++++++++- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index b093ff3e5..7b07cb4c7 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -1,9 +1,11 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + [SupportedOSPlatform("macos")] static class FormatTable { private static readonly MTLPixelFormat[] _table; @@ -167,7 +169,17 @@ namespace Ryujinx.Graphics.Metal public static MTLPixelFormat GetFormat(Format format) { - return _table[(int)format]; + var mtlFormat = _table[(int)format]; + + if (mtlFormat == MTLPixelFormat.Depth24UnormStencil8 || mtlFormat == MTLPixelFormat.Depth32FloatStencil8) + { + if (!MTLDevice.CreateSystemDefaultDevice().Depth24Stencil8PixelFormatSupported) + { + mtlFormat = MTLPixelFormat.Depth32Float; + } + } + + return mtlFormat; } } } diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index d8607a618..7074e7da3 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void CopyTo(BufferRange range, int layer, int level, int stride) -- 2.47.1 From d36c285b79302fce81de311588dbde8a3b9e7a56 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 21:53:49 -0400 Subject: [PATCH 019/368] Undertale boots --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 4 ++-- src/Ryujinx.Graphics.Metal/Program.cs | 22 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Program.cs diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 434a73747..09960f9ab 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -86,8 +86,8 @@ namespace Ryujinx.Graphics.Metal public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { - var library = _device.NewDefaultLibrary(); - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + return new Program(); } public ISampler CreateSampler(SamplerCreateInfo info) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs new file mode 100644 index 000000000..add16462f --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Metal +{ + public class Program : IProgram + { + public void Dispose() + { + return; + } + + public ProgramLinkStatus CheckProgramLink(bool blocking) + { + return ProgramLinkStatus.Failure; + } + + public byte[] GetBinary() + { + return new byte[] {}; + } + } +} -- 2.47.1 From fb8749ce4ec4b77da99b03d801d831c3e1ec37c3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 08:48:41 -0400 Subject: [PATCH 020/368] HelperShaders class --- src/Ryujinx.Graphics.Metal/HelperShaders.cs | 59 +++++++++++++++++++ ...Source.metal => HelperShadersSource.metal} | 6 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 4 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 40 ++----------- .../Ryujinx.Graphics.Metal.csproj | 8 +-- 5 files changed, 72 insertions(+), 45 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/HelperShaders.cs rename src/Ryujinx.Graphics.Metal/{Shaders/ColorBlitShaderSource.metal => HelperShadersSource.metal} (77%) diff --git a/src/Ryujinx.Graphics.Metal/HelperShaders.cs b/src/Ryujinx.Graphics.Metal/HelperShaders.cs new file mode 100644 index 000000000..0864839fd --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/HelperShaders.cs @@ -0,0 +1,59 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class HelperShaders + { + private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/HelperShadersSource.metal"; + + public HelperShader BlitShader; + + public HelperShaders(MTLDevice device) + { + var error = new NSError(IntPtr.Zero); + + var shaderSource = EmbeddedResources.ReadAllText(ShadersSourcePath); + var library = device.NewLibrary(StringHelper.NSString(shaderSource), new(IntPtr.Zero), ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Library: {StringHelper.String(error.LocalizedDescription)}"); + } + + BlitShader = new HelperShader(device, library, "vertexBlit", "fragmentBlit"); + } + } + + [SupportedOSPlatform("macos")] + public struct HelperShader + { + private MTLRenderPipelineState _pipelineState; + public static implicit operator MTLRenderPipelineState(HelperShader shader) => shader._pipelineState; + + public HelperShader(MTLDevice device, MTLLibrary library, string vertex, string fragment) + { + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + + renderPipelineDescriptor.VertexFunction = library.NewFunction(StringHelper.NSString(vertex));; + renderPipelineDescriptor.FragmentFunction = library.NewFunction(StringHelper.NSString(fragment)); + renderPipelineDescriptor.ColorAttachments.Object(0).SetBlendingEnabled(true); + renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; + renderPipelineDescriptor.ColorAttachments.Object(0).SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + + var error = new NSError(IntPtr.Zero); + _pipelineState = device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal b/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal similarity index 77% rename from src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal rename to src/Ryujinx.Graphics.Metal/HelperShadersSource.metal index a1630e7b8..7d3c9eb89 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal +++ b/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal @@ -16,7 +16,7 @@ struct CopyVertexOut { float2 uv; }; -vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) { +vertex CopyVertexOut vertexBlit(unsigned short vid [[vertex_id]]) { float2 position = quadVertices[vid]; CopyVertexOut out; @@ -28,8 +28,8 @@ vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) { return out; } -fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], - texture2d tex) { +fragment float4 fragmentBlit(CopyVertexOut in [[stage_in]], + texture2d tex) { constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); return tex.sample(sam, in.uv).xyzw; diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 09960f9ab..d3d73f551 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -46,12 +46,12 @@ namespace Ryujinx.Graphics.Metal MTLCaptureManager.SharedCaptureManager().StartCapture(captureDescriptor, ref captureError); if (captureError != IntPtr.Zero) { - Console.Write($"Failed to start capture! {StringHelper.String(captureError.LocalizedDescription)}"); + Console.WriteLine($"Failed to start capture! {StringHelper.String(captureError.LocalizedDescription)}"); } _window = new Window(this, layer); - _pipeline = new Pipeline(_device, _queue, layer); + _pipeline = new Pipeline(_device, _queue); } public void BackgroundContextAction(Action action, bool alwaysBackground = false) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 47d8f52ff..f101fa637 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; @@ -14,10 +13,9 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public class Pipeline : IPipeline, IDisposable { - private const string ShaderSourcePath = "Ryujinx.Graphics.Metal/Shaders"; - private readonly MTLDevice _device; private readonly MTLCommandQueue _mtlCommandQueue; + private readonly HelperShaders _helperShaders; private MTLCommandBuffer _commandBuffer; private MTLCommandEncoder _currentEncoder; @@ -32,43 +30,13 @@ namespace Ryujinx.Graphics.Metal private MTLClearColor _clearColor; private int frameCount = 0; - public Pipeline(MTLDevice device, MTLCommandQueue commandQueue, CAMetalLayer metalLayer) + public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { _device = device; _mtlCommandQueue = commandQueue; + _helperShaders = new HelperShaders(_device); - var error = new NSError(IntPtr.Zero); - - var shaderSource = EmbeddedResources.ReadAllText(string.Join('/', ShaderSourcePath, "ColorBlitShaderSource.metal")); - var library = _device.NewLibrary(StringHelper.NSString(shaderSource), new(IntPtr.Zero), ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Library: {StringHelper.String(error.LocalizedDescription)}"); - } - - var vertexFunction = library.NewFunction(StringHelper.NSString("vertexMain")); - var fragmentFunction = library.NewFunction(StringHelper.NSString("fragmentMain")); - - // TODO: Recreate descriptor and encoder state as needed - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - renderPipelineDescriptor.VertexFunction = vertexFunction; - renderPipelineDescriptor.FragmentFunction = fragmentFunction; - // TODO: This should not be hardcoded, but a bug in SharpMetal prevents me from doing this correctly - renderPipelineDescriptor.ColorAttachments.Object(0).SetBlendingEnabled(true); - renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; - renderPipelineDescriptor.ColorAttachments.Object(0).SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - - var renderPipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - _renderEncoderState = new RenderEncoderState(renderPipelineState, _device); - // + _renderEncoderState = new RenderEncoderState(_helperShaders.BlitShader, _device); _commandBuffer = _mtlCommandQueue.CommandBuffer(); } diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 638e1db67..529666b9d 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -10,12 +10,12 @@ - - - - + + + + \ No newline at end of file -- 2.47.1 From 0edec0d3ffc16344092c49d64427415e945dcb83 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 08:50:36 -0400 Subject: [PATCH 021/368] Shader comments --- src/Ryujinx.Graphics.Metal/HelperShadersSource.metal | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal b/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal index 7d3c9eb89..7b7dddad0 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal +++ b/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal @@ -2,6 +2,10 @@ using namespace metal; +// ------------------ +// Simple Blit Shader +// ------------------ + constant float2 quadVertices[] = { float2(-1, -1), float2(-1, 1), -- 2.47.1 From 25dba8da7ce23899bec4f974458b411620c2bdc4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 08:58:14 -0400 Subject: [PATCH 022/368] Pass sampler to Blit shader --- .../HelperShadersSource.metal | 7 +++---- src/Ryujinx.Graphics.Metal/Pipeline.cs | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal b/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal index 7b7dddad0..dd39c6a89 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal +++ b/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal @@ -33,8 +33,7 @@ vertex CopyVertexOut vertexBlit(unsigned short vid [[vertex_id]]) { } fragment float4 fragmentBlit(CopyVertexOut in [[stage_in]], - texture2d tex) { - constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); - - return tex.sample(sam, in.uv).xyzw; + texture2d texture [[texture(0)]], + sampler sampler [[sampler(0)]]) { + return texture.sample(sampler, in.uv); } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index f101fa637..c8607c5be 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -101,7 +101,15 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Began present"); _renderEncoderState.SetEncoderState(renderCommandEncoder); + var sampler = _device.NewSamplerState(new MTLSamplerDescriptor + { + MinFilter = MTLSamplerMinMagFilter.Nearest, + MagFilter = MTLSamplerMinMagFilter.Nearest, + MipFilter = MTLSamplerMipFilter.NotMipmapped + }); + renderCommandEncoder.SetFragmentTexture(texture.MTLTexture, 0); + renderCommandEncoder.SetFragmentSamplerState(sampler, 0); renderCommandEncoder.DrawPrimitives(MTLPrimitiveType.Triangle, 0, 6); renderCommandEncoder.EndEncoding(); @@ -199,6 +207,8 @@ namespace Ryujinx.Graphics.Metal public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { + Logger.Warning?.Print(LogClass.Gpu, "Draw"); + MTLRenderCommandEncoder renderCommandEncoder; if (_currentEncoder is MTLRenderCommandEncoder encoder) @@ -218,6 +228,8 @@ namespace Ryujinx.Graphics.Metal public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) { + Logger.Warning?.Print(LogClass.Gpu, "Draw"); + MTLRenderCommandEncoder renderCommandEncoder; if (_currentEncoder is MTLRenderCommandEncoder encoder) @@ -292,6 +304,8 @@ namespace Ryujinx.Graphics.Metal public void SetDepthTest(DepthTestDescriptor depthTest) { + Logger.Warning?.Print(LogClass.Gpu, "Set depth test"); + var depthStencilState = _renderEncoderState.UpdateDepthState( depthTest.TestEnable ? MTLCompareFunction.Always : depthTest.Func.Convert(), depthTest.WriteEnable); @@ -304,6 +318,8 @@ namespace Ryujinx.Graphics.Metal public void SetFaceCulling(bool enable, Face face) { + Logger.Warning?.Print(LogClass.Gpu, "Set face culling"); + var cullMode = enable ? face.Convert() : MTLCullMode.None; if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) @@ -316,6 +332,8 @@ namespace Ryujinx.Graphics.Metal public void SetFrontFace(FrontFace frontFace) { + Logger.Warning?.Print(LogClass.Gpu, "Set front face"); + var winding = frontFace.Convert(); if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) @@ -328,6 +346,8 @@ namespace Ryujinx.Graphics.Metal public void SetIndexBuffer(BufferRange buffer, IndexType type) { + Logger.Warning?.Print(LogClass.Gpu, "Set index buffer"); + if (buffer.Handle != BufferHandle.Null) { _indexType = type.Convert(); -- 2.47.1 From a7908c187d01c89546b8907a965ccc73a50e3ffe Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 09:21:32 -0400 Subject: [PATCH 023/368] Warn when generating unsupported shader --- .../CodeGen/Msl/CodeGenContext.cs | 2 +- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 1 - src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 9 ++++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index a84d99a04..f41e39625 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) { Info = info; - Config = Config; + Config = config; _sb = new StringBuilder(); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 47a8b477f..11d46278e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -1,4 +1,3 @@ -using Ryujinx.Graphics.Shader.CodeGen.Glsl; using Ryujinx.Graphics.Shader.StructuredIr; namespace Ryujinx.Graphics.Shader.CodeGen.Msl diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 0dc82390f..71196b124 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; @@ -7,7 +8,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { public static string Generate(StructuredProgramInfo info, ShaderConfig config) { - CodeGenContext context = new CodeGenContext(info, config); + if (config.Stage is not (ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute)) + { + Logger.Warning?.Print(LogClass.Gpu, $"Attempted to generate unsupported shader type {config.Stage}!"); + return ""; + } + + CodeGenContext context = new(info, config); Declarations.Declare(context, info); -- 2.47.1 From a8b4e643d09d121f5d3c0d9c791935cb1097654c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 11:51:11 -0400 Subject: [PATCH 024/368] Start of MSL instructions Remaining functions --- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 137 ++++++++++++++++++ .../CodeGen/Msl/Instructions/InstInfo.cs | 15 ++ .../CodeGen/Msl/Instructions/InstType.cs | 36 +++++ 3 files changed, 188 insertions(+) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs new file mode 100644 index 000000000..632ad6bf2 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -0,0 +1,137 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + static class InstGenHelper + { + private static readonly InstInfo[] _infoTable; + + static InstGenHelper() + { + _infoTable = new InstInfo[(int)Instruction.Count]; + +#pragma warning disable IDE0055 // Disable formatting + Add(Instruction.AtomicAdd, InstType.AtomicBinary, "add"); + Add(Instruction.AtomicAnd, InstType.AtomicBinary, "and"); + Add(Instruction.AtomicCompareAndSwap, 0); + Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "max"); + Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "min"); + Add(Instruction.AtomicOr, InstType.AtomicBinary, "or"); + Add(Instruction.AtomicSwap, 0); + Add(Instruction.AtomicXor, InstType.AtomicBinary, "xor"); + Add(Instruction.Absolute, InstType.AtomicBinary, "abs"); + Add(Instruction.Add, InstType.OpBinaryCom, "+"); + Add(Instruction.Ballot, InstType.Special); + Add(Instruction.Barrier, InstType.Special); + Add(Instruction.BitCount, InstType.CallUnary, "popcount"); + Add(Instruction.BitfieldExtractS32, InstType.CallTernary, "extract_bits"); + Add(Instruction.BitfieldExtractU32, InstType.CallTernary, "extract_bits"); + Add(Instruction.BitfieldInsert, InstType.CallQuaternary, "insert_bits"); + Add(Instruction.BitfieldReverse, InstType.CallUnary, "reverse_bits"); + Add(Instruction.BitwiseAnd, InstType.OpBinaryCom, "&"); + Add(Instruction.BitwiseExclusiveOr, InstType.OpBinaryCom, "^"); + Add(Instruction.BitwiseNot, InstType.OpUnary, "~"); + Add(Instruction.BitwiseOr, InstType.OpBinaryCom, "|"); + Add(Instruction.Call, InstType.Special); + Add(Instruction.Ceiling, InstType.CallUnary, "ceil"); + Add(Instruction.Clamp, InstType.CallTernary, "clamp"); + Add(Instruction.ClampU32, InstType.CallTernary, "clamp"); + Add(Instruction.CompareEqual, InstType.OpBinaryCom, "=="); + Add(Instruction.CompareGreater, InstType.OpBinary, ">"); + Add(Instruction.CompareGreaterOrEqual, InstType.OpBinary, ">="); + Add(Instruction.CompareGreaterOrEqualU32, InstType.OpBinary, ">="); + Add(Instruction.CompareGreaterU32, InstType.OpBinary, ">"); + Add(Instruction.CompareLess, InstType.OpBinary, "<"); + Add(Instruction.CompareLessOrEqual, InstType.OpBinary, "<="); + Add(Instruction.CompareLessOrEqualU32, InstType.OpBinary, "<="); + Add(Instruction.CompareLessU32, InstType.OpBinary, "<"); + Add(Instruction.CompareNotEqual, InstType.OpBinaryCom, "!="); + Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:"); + Add(Instruction.ConvertFP32ToFP64, 0); // MSL does not have a 64-bit FP + Add(Instruction.ConvertFP64ToFP32, 0); // MSL does not have a 64-bit FP + Add(Instruction.ConvertFP32ToS32, InstType.Cast, "int"); + Add(Instruction.ConvertFP32ToU32, InstType.Cast, "uint"); + Add(Instruction.ConvertFP64ToS32, 0); // MSL does not have a 64-bit FP + Add(Instruction.ConvertFP64ToU32, 0); // MSL does not have a 64-bit FP + Add(Instruction.ConvertS32ToFP32, InstType.Cast, "float"); + Add(Instruction.ConvertS32ToFP64, 0); // MSL does not have a 64-bit FP + Add(Instruction.ConvertU32ToFP32, InstType.Cast, "float"); + Add(Instruction.ConvertU32ToFP64, 0); // MSL does not have a 64-bit FP + Add(Instruction.Cosine, InstType.CallUnary, "cos"); + Add(Instruction.Ddx, InstType.CallUnary, "dfdx"); + Add(Instruction.Ddy, InstType.CallUnary, "dfdy"); + Add(Instruction.Discard, InstType.CallNullary, "discard_fragment"); + Add(Instruction.Divide, InstType.OpBinary, "/"); + Add(Instruction.EmitVertex, 0); // MSL does not have geometry shaders + Add(Instruction.EndPrimitive, 0); // MSL does not have geometry shaders + Add(Instruction.ExponentB2, InstType.CallUnary, "exp2"); + Add(Instruction.FSIBegin, InstType.Special); + Add(Instruction.FSIEnd, InstType.Special); + // TODO: LSB and MSB Implementations https://github.com/KhronosGroup/SPIRV-Cross/blob/bccaa94db814af33d8ef05c153e7c34d8bd4d685/reference/shaders-msl-no-opt/asm/comp/bitscan.asm.comp#L8 + Add(Instruction.FindLSB, InstType.Special); + Add(Instruction.FindMSBS32, InstType.Special); + Add(Instruction.FindMSBU32, InstType.Special); + Add(Instruction.Floor, InstType.CallUnary, "floor"); + Add(Instruction.FusedMultiplyAdd, InstType.CallTernary, "fma"); + Add(Instruction.GroupMemoryBarrier, InstType.Special); + Add(Instruction.ImageLoad, InstType.Special); + Add(Instruction.ImageStore, InstType.Special); + Add(Instruction.ImageAtomic, InstType.Special); // Metal 3.1+ + Add(Instruction.IsNan, InstType.CallUnary, "isnan"); + Add(Instruction.Load, InstType.Special); + Add(Instruction.Lod, InstType.Special); + Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); + Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&"); + Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^"); + Add(Instruction.LogicalNot, InstType.OpUnary, "!"); + Add(Instruction.LogicalOr, InstType.OpBinaryCom, "||"); + Add(Instruction.LoopBreak, InstType.OpNullary, "break"); + Add(Instruction.LoopContinue, InstType.OpNullary, "continue"); + Add(Instruction.PackDouble2x32, 0); // MSL does not have a 64-bit FP + Add(Instruction.PackHalf2x16, InstType.CallUnary, "pack_half_to_unorm2x16"); + Add(Instruction.Maximum, InstType.CallBinary, "max"); + Add(Instruction.MaximumU32, InstType.CallBinary, "max"); + Add(Instruction.MemoryBarrier, InstType.Special); + Add(Instruction.Minimum, InstType.CallBinary, "min"); + Add(Instruction.MinimumU32, InstType.CallBinary, "min"); + Add(Instruction.Modulo, InstType.CallBinary, "%"); + Add(Instruction.Multiply, InstType.OpBinaryCom, "*"); + Add(Instruction.MultiplyHighS32, InstType.Special); + Add(Instruction.MultiplyHighU32, InstType.Special); + Add(Instruction.Negate, InstType.Special); + Add(Instruction.ReciprocalSquareRoot, InstType.CallUnary, "rsqrt"); + Add(Instruction.Return, InstType.OpNullary, "return"); + Add(Instruction.Round, InstType.CallUnary, "round"); + Add(Instruction.ShiftLeft, InstType.OpBinary, "<<"); + Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>"); + Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>"); + // TODO: Shuffle funcs + Add(Instruction.Shuffle, 0); + Add(Instruction.ShuffleDown, 0); + Add(Instruction.ShuffleUp, 0); + Add(Instruction.ShuffleXor, 0); + Add(Instruction.Sine, InstType.CallUnary, "sin"); + Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); + Add(Instruction.Store, InstType.Special); + Add(Instruction.Subtract, InstType.OpBinary, "-"); + // TODO: Swizzle add + Add(Instruction.SwizzleAdd, InstType.Special); + Add(Instruction.TextureSample, InstType.Special); + Add(Instruction.TextureSize, InstType.Special); + Add(Instruction.Truncate, InstType.CallUnary, "trunc"); + Add(Instruction.UnpackDouble2x32, 0); // MSL does not have a 64-bit FP + Add(Instruction.UnpackHalf2x16, InstType.CallUnary, "unpack_unorm2x16_to_half"); + Add(Instruction.VectorExtract, InstType.Special); + Add(Instruction.VoteAll, InstType.CallUnary, "simd_all"); + // TODO: https://github.com/KhronosGroup/SPIRV-Cross/blob/bccaa94db814af33d8ef05c153e7c34d8bd4d685/reference/shaders-msl/comp/shader_group_vote.msl21.comp#L9 + Add(Instruction.VoteAllEqual, InstType.Special); + Add(Instruction.VoteAny, InstType.CallUnary, "simd_any"); +#pragma warning restore IDE0055 + } + + private static void Add(Instruction inst, InstType flags, string opName = null) + { + _infoTable[(int)inst] = new InstInfo(flags, opName); + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs new file mode 100644 index 000000000..62cbda199 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + readonly struct InstInfo + { + public InstType Type { get; } + + public string OpName { get; } + + public InstInfo(InstType type, string opName) + { + Type = type; + OpName = opName; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs new file mode 100644 index 000000000..85930cb24 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] + public enum InstType + { + OpNullary = Op | 0, + OpUnary = Op | 1, + OpBinary = Op | 2, + OpBinaryCom = Op | 2 | Commutative, + OpTernary = Op | 3, + + CallNullary = Call | 0, + CallUnary = Call | 1, + CallBinary = Call | 2, + CallTernary = Call | 3, + CallQuaternary = Call | 4, + + // The atomic instructions have one extra operand, + // for the storage slot and offset pair. + AtomicBinary = Call | Atomic | 3, + AtomicTernary = Call | Atomic | 4, + + Commutative = 1 << 8, + Op = 1 << 9, + Call = 1 << 10, + Atomic = 1 << 11, + Special = 1 << 12, + Cast = 1 << 13, + + ArityMask = 0xff, + } +} -- 2.47.1 From 7441d94f107d287ad798af5afeacfb12b2a8a4f3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 14:50:49 -0400 Subject: [PATCH 025/368] Formatting --- Directory.Packages.props.orig | 67 + src/Ryujinx.Graphics.Metal/CounterEvent.cs | 2 +- .../FormatCapabilities.cs | 14 - src/Ryujinx.Graphics.Metal/HardwareInfo.cs | 14 +- src/Ryujinx.Graphics.Metal/HelperShaders.cs | 13 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 12 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 13 +- src/Ryujinx.Graphics.Metal/Program.cs | 14 +- .../RenderEncoderState.cs | 19 +- src/Ryujinx.Graphics.Metal/Sampler.cs | 6 +- src/Ryujinx.Graphics.Metal/Texture.cs | 16 +- src/Ryujinx.Graphics.Metal/Window.cs | 2 +- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 32 +- src/Ryujinx/AppHost.cs | 17 +- src/Ryujinx/AppHost.cs.orig | 1226 +++++++++++++++++ 15 files changed, 1386 insertions(+), 81 deletions(-) create mode 100644 Directory.Packages.props.orig delete mode 100644 src/Ryujinx.Graphics.Metal/FormatCapabilities.cs create mode 100644 src/Ryujinx/AppHost.cs.orig diff --git a/Directory.Packages.props.orig b/Directory.Packages.props.orig new file mode 100644 index 000000000..8f22ea60c --- /dev/null +++ b/Directory.Packages.props.orig @@ -0,0 +1,67 @@ + + + true + + + + + + + + + + + + + + + + + + + +<<<<<<< HEAD + + + +======= + + + +>>>>>>> 45a6dffcf (Bump SharpMetal) + + + + + + + + + + + + + + + + + + + + + +<<<<<<< HEAD + + + +======= + + + + +>>>>>>> 45a6dffcf (Bump SharpMetal) + + + + + \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/CounterEvent.cs b/src/Ryujinx.Graphics.Metal/CounterEvent.cs index eec810991..1773b9b63 100644 --- a/src/Ryujinx.Graphics.Metal/CounterEvent.cs +++ b/src/Ryujinx.Graphics.Metal/CounterEvent.cs @@ -2,7 +2,7 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Metal { - public class CounterEvent : ICounterEvent + class CounterEvent : ICounterEvent { public CounterEvent() diff --git a/src/Ryujinx.Graphics.Metal/FormatCapabilities.cs b/src/Ryujinx.Graphics.Metal/FormatCapabilities.cs deleted file mode 100644 index 6dc56c59b..000000000 --- a/src/Ryujinx.Graphics.Metal/FormatCapabilities.cs +++ /dev/null @@ -1,14 +0,0 @@ -using SharpMetal; - -namespace Ryujinx.Graphics.Metal -{ - static class FormatCapabilities - { - public static MTLPixelFormat ConvertToMTLFormat(GAL.Format srcFormat) - { - var format = FormatTable.GetFormat(srcFormat); - - return format; - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs b/src/Ryujinx.Graphics.Metal/HardwareInfo.cs index 3ca7cdfca..13566dbd8 100644 --- a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs +++ b/src/Ryujinx.Graphics.Metal/HardwareInfo.cs @@ -6,8 +6,8 @@ namespace Ryujinx.Graphics.Metal static partial class HardwareInfoTools { - private readonly static IntPtr kCFAllocatorDefault = IntPtr.Zero; - private readonly static UInt32 kCFStringEncodingASCII = 0x0600; + private readonly static IntPtr _kCFAllocatorDefault = IntPtr.Zero; + private readonly static UInt32 _kCFStringEncodingASCII = 0x0600; private const string IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"; private const string CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; @@ -47,8 +47,8 @@ namespace Ryujinx.Graphics.Metal { var serviceDict = IOServiceMatching("IOGPU"); var service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict); - var cfString = CFStringCreateWithCString(kCFAllocatorDefault, "vendor-id", kCFStringEncodingASCII); - var cfProperty = IORegistryEntryCreateCFProperty(service, cfString, kCFAllocatorDefault, 0); + var cfString = CFStringCreateWithCString(_kCFAllocatorDefault, "vendor-id", _kCFStringEncodingASCII); + var cfProperty = IORegistryEntryCreateCFProperty(service, cfString, _kCFAllocatorDefault, 0); byte[] buffer = new byte[4]; var bufferPtr = CFDataGetBytePtr(cfProperty); @@ -63,13 +63,13 @@ namespace Ryujinx.Graphics.Metal { var serviceDict = IOServiceMatching("IOGPU"); var service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict); - var cfString = CFStringCreateWithCString(kCFAllocatorDefault, "model", kCFStringEncodingASCII); - var cfProperty = IORegistryEntryCreateCFProperty(service, cfString, kCFAllocatorDefault, 0); + var cfString = CFStringCreateWithCString(_kCFAllocatorDefault, "model", _kCFStringEncodingASCII); + var cfProperty = IORegistryEntryCreateCFProperty(service, cfString, _kCFAllocatorDefault, 0); char[] buffer = new char[64]; IntPtr bufferPtr = Marshal.AllocHGlobal(buffer.Length); - if (CFStringGetCString(cfProperty, bufferPtr, buffer.Length, kCFStringEncodingASCII)) + if (CFStringGetCString(cfProperty, bufferPtr, buffer.Length, _kCFStringEncodingASCII)) { var model = Marshal.PtrToStringUTF8(bufferPtr); Marshal.FreeHGlobal(bufferPtr); diff --git a/src/Ryujinx.Graphics.Metal/HelperShaders.cs b/src/Ryujinx.Graphics.Metal/HelperShaders.cs index 0864839fd..a4517b7ce 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShaders.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShaders.cs @@ -30,17 +30,18 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - public struct HelperShader + public readonly struct HelperShader { - private MTLRenderPipelineState _pipelineState; + private readonly MTLRenderPipelineState _pipelineState; public static implicit operator MTLRenderPipelineState(HelperShader shader) => shader._pipelineState; public HelperShader(MTLDevice device, MTLLibrary library, string vertex, string fragment) { - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - - renderPipelineDescriptor.VertexFunction = library.NewFunction(StringHelper.NSString(vertex));; - renderPipelineDescriptor.FragmentFunction = library.NewFunction(StringHelper.NSString(fragment)); + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor + { + VertexFunction = library.NewFunction(StringHelper.NSString(vertex)), + FragmentFunction = library.NewFunction(StringHelper.NSString(fragment)) + }; renderPipelineDescriptor.ColorAttachments.Object(0).SetBlendingEnabled(true); renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; renderPipelineDescriptor.ColorAttachments.Object(0).SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index d3d73f551..21c8c49b4 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -38,10 +38,12 @@ namespace Ryujinx.Graphics.Metal var layer = _getMetalLayer(); layer.Device = _device; - var captureDescriptor = new MTLCaptureDescriptor(); - captureDescriptor.CaptureObject = _queue; - captureDescriptor.Destination = MTLCaptureDestination.GPUTraceDocument; - captureDescriptor.OutputURL = NSURL.FileURLWithPath(StringHelper.NSString("/Users/isaacmarovitz/Desktop/Trace.gputrace")); + var captureDescriptor = new MTLCaptureDescriptor + { + CaptureObject = _queue, + Destination = MTLCaptureDestination.GPUTraceDocument, + OutputURL = NSURL.FileURLWithPath(StringHelper.NSString("/Users/isaacmarovitz/Desktop/Trace.gputrace")) + }; var captureError = new NSError(IntPtr.Zero); MTLCaptureManager.SharedCaptureManager().StartCapture(captureDescriptor, ref captureError); if (captureError != IntPtr.Zero) @@ -216,7 +218,7 @@ namespace Ryujinx.Graphics.Metal { MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); var span = new Span(mtlBuffer.Contents.ToPointer(), (int)mtlBuffer.Length); - data.CopyTo(span.Slice(offset)); + data.CopyTo(span[offset..]); mtlBuffer.DidModifyRange(new NSRange { location = (ulong)offset, diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index c8607c5be..6a3d9a801 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -11,7 +11,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class Pipeline : IPipeline, IDisposable + class Pipeline : IPipeline, IDisposable { private readonly MTLDevice _device; private readonly MTLCommandQueue _mtlCommandQueue; @@ -88,8 +88,13 @@ namespace Ryujinx.Graphics.Metal return computeCommandEncoder; } - public void Present(CAMetalDrawable drawable, Texture texture) + public void Present(CAMetalDrawable drawable, ITexture texture) { + if (texture is not Texture tex) + { + return; + } + EndCurrentPass(); var descriptor = new MTLRenderPassDescriptor(); @@ -108,7 +113,7 @@ namespace Ryujinx.Graphics.Metal MipFilter = MTLSamplerMipFilter.NotMipmapped }); - renderCommandEncoder.SetFragmentTexture(texture.MTLTexture, 0); + renderCommandEncoder.SetFragmentTexture(tex.MTLTexture, 0); renderCommandEncoder.SetFragmentSamplerState(sampler, 0); renderCommandEncoder.DrawPrimitives(MTLPrimitiveType.Triangle, 0, 6); @@ -162,7 +167,7 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - _clearColor = new MTLClearColor { red = color.Red, green = color.Green, blue = color.Blue, alpha = color.Alpha}; + _clearColor = new MTLClearColor { red = color.Red, green = color.Green, blue = color.Blue, alpha = color.Alpha }; } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index add16462f..0a748bfcf 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -2,13 +2,8 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Metal { - public class Program : IProgram + class Program : IProgram { - public void Dispose() - { - return; - } - public ProgramLinkStatus CheckProgramLink(bool blocking) { return ProgramLinkStatus.Failure; @@ -16,7 +11,12 @@ namespace Ryujinx.Graphics.Metal public byte[] GetBinary() { - return new byte[] {}; + return ""u8.ToArray(); + } + + public void Dispose() + { + return; } } } diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index db946d8b8..e24b49090 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; +using System; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -7,8 +8,9 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] struct RenderEncoderState { - private MTLDevice _device; - + private readonly MTLDevice _device; + // TODO: Work with more than one pipeline state + private readonly MTLRenderPipelineState _copyPipeline; private MTLDepthStencilState _depthStencilState = null; private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always; @@ -17,7 +19,6 @@ namespace Ryujinx.Graphics.Metal private MTLStencilDescriptor _backFaceStencil = null; private MTLStencilDescriptor _frontFaceStencil = null; - public MTLRenderPipelineState CopyPipeline; public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.Clockwise; @@ -25,15 +26,19 @@ namespace Ryujinx.Graphics.Metal public RenderEncoderState(MTLRenderPipelineState copyPipeline, MTLDevice device) { _device = device; - CopyPipeline = copyPipeline; + _copyPipeline = copyPipeline; } - public void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder) + public readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder) { - renderCommandEncoder.SetRenderPipelineState(CopyPipeline); + renderCommandEncoder.SetRenderPipelineState(_copyPipeline); renderCommandEncoder.SetCullMode(CullMode); renderCommandEncoder.SetFrontFacingWinding(Winding); - // renderCommandEncoder.SetDepthStencilState(_depthStencilState); + + if (_depthStencilState != null) + { + renderCommandEncoder.SetDepthStencilState(_depthStencilState); + } } public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace) diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index a40040c5f..cc1923cc3 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -3,13 +3,13 @@ using SharpMetal.Metal; namespace Ryujinx.Graphics.Metal { - public class Sampler : ISampler + class Sampler : ISampler { - private MTLSamplerState _mtlSamplerState; + // private readonly MTLSamplerState _mtlSamplerState; public Sampler(MTLSamplerState mtlSamplerState) { - _mtlSamplerState = mtlSamplerState; + // _mtlSamplerState = mtlSamplerState; } public void Dispose() diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 7074e7da3..ea3a8cba8 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -9,7 +9,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class Texture : ITexture, IDisposable + class Texture : ITexture, IDisposable { private readonly TextureCreateInfo _info; private readonly Pipeline _pipeline; @@ -28,12 +28,14 @@ namespace Ryujinx.Graphics.Metal _pipeline = pipeline; _info = info; - var descriptor = new MTLTextureDescriptor(); - descriptor.PixelFormat = FormatTable.GetFormat(Info.Format); - descriptor.Usage = MTLTextureUsage.ShaderRead | MTLTextureUsage.ShaderWrite | MTLTextureUsage.RenderTarget; - descriptor.Width = (ulong)Width; - descriptor.Height = (ulong)Height; - descriptor.Depth = (ulong)Depth; + var descriptor = new MTLTextureDescriptor + { + PixelFormat = FormatTable.GetFormat(Info.Format), + Usage = MTLTextureUsage.ShaderRead | MTLTextureUsage.ShaderWrite | MTLTextureUsage.RenderTarget, + Width = (ulong)Width, + Height = (ulong)Height, + Depth = (ulong)Depth + }; descriptor.Depth = (ulong)Info.Depth; descriptor.SampleCount = (ulong)Info.Samples; descriptor.MipmapLevelCount = (ulong)Info.Levels; diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 563f888af..f8ddca3fe 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -8,7 +8,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class Window : IWindow, IDisposable + class Window : IWindow, IDisposable { private readonly MetalRenderer _renderer; private readonly CAMetalLayer _metalLayer; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 632ad6bf2..c8757ad35 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -106,26 +106,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>"); Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>"); // TODO: Shuffle funcs - Add(Instruction.Shuffle, 0); - Add(Instruction.ShuffleDown, 0); - Add(Instruction.ShuffleUp, 0); - Add(Instruction.ShuffleXor, 0); - Add(Instruction.Sine, InstType.CallUnary, "sin"); - Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); - Add(Instruction.Store, InstType.Special); - Add(Instruction.Subtract, InstType.OpBinary, "-"); + Add(Instruction.Shuffle, 0); + Add(Instruction.ShuffleDown, 0); + Add(Instruction.ShuffleUp, 0); + Add(Instruction.ShuffleXor, 0); + Add(Instruction.Sine, InstType.CallUnary, "sin"); + Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); + Add(Instruction.Store, InstType.Special); + Add(Instruction.Subtract, InstType.OpBinary, "-"); // TODO: Swizzle add - Add(Instruction.SwizzleAdd, InstType.Special); - Add(Instruction.TextureSample, InstType.Special); - Add(Instruction.TextureSize, InstType.Special); - Add(Instruction.Truncate, InstType.CallUnary, "trunc"); + Add(Instruction.SwizzleAdd, InstType.Special); + Add(Instruction.TextureSample, InstType.Special); + Add(Instruction.TextureSize, InstType.Special); + Add(Instruction.Truncate, InstType.CallUnary, "trunc"); Add(Instruction.UnpackDouble2x32, 0); // MSL does not have a 64-bit FP - Add(Instruction.UnpackHalf2x16, InstType.CallUnary, "unpack_unorm2x16_to_half"); + Add(Instruction.UnpackHalf2x16, InstType.CallUnary, "unpack_unorm2x16_to_half"); Add(Instruction.VectorExtract, InstType.Special); - Add(Instruction.VoteAll, InstType.CallUnary, "simd_all"); + Add(Instruction.VoteAll, InstType.CallUnary, "simd_all"); // TODO: https://github.com/KhronosGroup/SPIRV-Cross/blob/bccaa94db814af33d8ef05c153e7c34d8bd4d685/reference/shaders-msl/comp/shader_group_vote.msl21.comp#L9 - Add(Instruction.VoteAllEqual, InstType.Special); - Add(Instruction.VoteAny, InstType.CallUnary, "simd_any"); + Add(Instruction.VoteAllEqual, InstType.Special); + Add(Instruction.VoteAny, InstType.CallUnary, "simd_any"); #pragma warning restore IDE0055 } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 9cd44addb..5747b450e 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -894,12 +894,23 @@ namespace Ryujinx.Ava VirtualFileSystem.ReloadKeySet(); // Initialize Renderer. - IRenderer renderer = ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl - ? new OpenGLRenderer() - : VulkanRenderer.Create( + IRenderer renderer; + + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) + { + renderer = VulkanRenderer.Create( ConfigurationState.Instance.Graphics.PreferredGpu, (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface, VulkanHelper.GetRequiredInstanceExtensions); + } + else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal && OperatingSystem.IsMacOS()) + { + renderer = new MetalRenderer(); + } + else + { + renderer = new OpenGLRenderer(); + } BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; diff --git a/src/Ryujinx/AppHost.cs.orig b/src/Ryujinx/AppHost.cs.orig new file mode 100644 index 000000000..302ef914c --- /dev/null +++ b/src/Ryujinx/AppHost.cs.orig @@ -0,0 +1,1226 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Input; +using Avalonia.Threading; +using LibHac.Tools.FsSystem; +using Ryujinx.Audio.Backends.Dummy; +using Ryujinx.Audio.Backends.OpenAL; +using Ryujinx.Audio.Backends.SDL2; +using Ryujinx.Audio.Backends.SoundIo; +using Ryujinx.Audio.Integration; +using Ryujinx.Ava.Common; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Input; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Renderer; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Multiplayer; +using Ryujinx.Common.Logging; +using Ryujinx.Common.SystemInterop; +using Ryujinx.Common.Utilities; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Multithreading; +using Ryujinx.Graphics.Gpu; +using Ryujinx.Graphics.OpenGL; +using Ryujinx.Graphics.Vulkan; +using Ryujinx.Graphics.Metal; +using Ryujinx.HLE; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.Input; +using Ryujinx.Input.HLE; +using Ryujinx.UI.App.Common; +using Ryujinx.UI.Common; +using Ryujinx.UI.Common.Configuration; +using Ryujinx.UI.Common.Helper; +using Silk.NET.Vulkan; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SPB.Graphics.Vulkan; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; +using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; +using Image = SixLabors.ImageSharp.Image; +using InputManager = Ryujinx.Input.HLE.InputManager; +using IRenderer = Ryujinx.Graphics.GAL.IRenderer; +using Key = Ryujinx.Input.Key; +using MouseButton = Ryujinx.Input.MouseButton; +using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; +using Size = Avalonia.Size; +using Switch = Ryujinx.HLE.Switch; + +namespace Ryujinx.Ava +{ + internal class AppHost + { + private const int CursorHideIdleTime = 5; // Hide Cursor seconds. + private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. + private const int TargetFps = 60; + private const float VolumeDelta = 0.05f; + + private static readonly Cursor _invisibleCursor = new(StandardCursorType.None); + private readonly IntPtr _invisibleCursorWin; + private readonly IntPtr _defaultCursorWin; + + private readonly long _ticksPerFrame; + private readonly Stopwatch _chrono; + private long _ticks; + + private readonly AccountManager _accountManager; + private readonly UserChannelPersistence _userChannelPersistence; + private readonly InputManager _inputManager; + + private readonly MainWindowViewModel _viewModel; + private readonly IKeyboard _keyboardInterface; + private readonly TopLevel _topLevel; + public RendererHost RendererHost; + + private readonly GraphicsDebugLevel _glLogLevel; + private float _newVolume; + private KeyboardHotkeyState _prevHotkeyState; + + private long _lastCursorMoveTime; + private bool _isCursorInRenderer = true; + + private bool _isStopped; + private bool _isActive; + private bool _renderingStarted; + + private readonly ManualResetEvent _gpuDoneEvent; + + private IRenderer _renderer; + private readonly Thread _renderingThread; + private readonly CancellationTokenSource _gpuCancellationTokenSource; + private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; + + private bool _dialogShown; + private readonly bool _isFirmwareTitle; + + private readonly object _lockObject = new(); + + public event EventHandler AppExit; + public event EventHandler StatusInitEvent; + public event EventHandler StatusUpdatedEvent; + + public VirtualFileSystem VirtualFileSystem { get; } + public ContentManager ContentManager { get; } + public NpadManager NpadManager { get; } + public TouchScreenManager TouchScreenManager { get; } + public Switch Device { get; set; } + + public int Width { get; private set; } + public int Height { get; private set; } + public string ApplicationPath { get; private set; } + public bool ScreenshotRequested { get; set; } + + public AppHost( + RendererHost renderer, + InputManager inputManager, + string applicationPath, + VirtualFileSystem virtualFileSystem, + ContentManager contentManager, + AccountManager accountManager, + UserChannelPersistence userChannelPersistence, + MainWindowViewModel viewmodel, + TopLevel topLevel) + { + _viewModel = viewmodel; + _inputManager = inputManager; + _accountManager = accountManager; + _userChannelPersistence = userChannelPersistence; + _renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" }; + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; + _topLevel = topLevel; + + _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer)); + + _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); + + NpadManager = _inputManager.CreateNpadManager(); + TouchScreenManager = _inputManager.CreateTouchScreenManager(); + ApplicationPath = applicationPath; + VirtualFileSystem = virtualFileSystem; + ContentManager = contentManager; + + RendererHost = renderer; + + _chrono = new Stopwatch(); + _ticksPerFrame = Stopwatch.Frequency / TargetFps; + + if (ApplicationPath.StartsWith("@SystemContent")) + { + ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); + + _isFirmwareTitle = true; + } + + ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; + + _topLevel.PointerMoved += TopLevel_PointerEnteredOrMoved; + _topLevel.PointerEntered += TopLevel_PointerEnteredOrMoved; + _topLevel.PointerExited += TopLevel_PointerExited; + + if (OperatingSystem.IsWindows()) + { + _invisibleCursorWin = CreateEmptyCursor(); + _defaultCursorWin = CreateArrowCursor(); + } + + ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; + ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; + ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; + ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough; + + ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState; + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; + ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState; + + _gpuCancellationTokenSource = new CancellationTokenSource(); + _gpuDoneEvent = new ManualResetEvent(false); + } + + private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e) + { + if (sender is MainWindow window) + { + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + + var point = e.GetCurrentPoint(window).Position; + var bounds = RendererHost.EmbeddedWindow.Bounds; + + _isCursorInRenderer = point.X >= bounds.X && + point.X <= bounds.Width + bounds.X && + point.Y >= bounds.Y && + point.Y <= bounds.Height + bounds.Y; + } + } + + private void TopLevel_PointerExited(object sender, PointerEventArgs e) + { + _isCursorInRenderer = false; + } + + private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs e) + { + _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + } + + private void UpdateScalingFilter(object sender, ReactiveEventArgs e) + { + _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + } + + private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs e) + { + _renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); + } + + private void ShowCursor() + { + Dispatcher.UIThread.Post(() => + { + _viewModel.Cursor = Cursor.Default; + + if (OperatingSystem.IsWindows()) + { + SetCursor(_defaultCursorWin); + } + }); + } + + private void HideCursor() + { + Dispatcher.UIThread.Post(() => + { + _viewModel.Cursor = _invisibleCursor; + + if (OperatingSystem.IsWindows()) + { + SetCursor(_invisibleCursorWin); + } + }); + } + + private void SetRendererWindowSize(Size size) + { + if (_renderer != null) + { + double scale = _topLevel.RenderScaling; + + _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); + } + } + + private void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e) + { + if (e.Data.Length > 0 && e.Height > 0 && e.Width > 0) + { + Task.Run(() => + { + lock (_lockObject) + { + string applicationName = Device.Processes.ActiveApplication.Name; + string sanitizedApplicationName = FileSystemUtils.SanitizeFileName(applicationName); + DateTime currentTime = DateTime.Now; + + string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; + + string directory = AppDataManager.Mode switch + { + AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), + _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), + }; + + string path = Path.Combine(directory, filename); + + try + { + Directory.CreateDirectory(directory); + } + catch (Exception ex) + { + Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {directory}. Error : {ex.GetType().Name}", "Screenshot"); + + return; + } + + Image image = e.IsBgra ? Image.LoadPixelData(e.Data, e.Width, e.Height) + : Image.LoadPixelData(e.Data, e.Width, e.Height); + + if (e.FlipX) + { + image.Mutate(x => x.Flip(FlipMode.Horizontal)); + } + + if (e.FlipY) + { + image.Mutate(x => x.Flip(FlipMode.Vertical)); + } + + image.SaveAsPng(path, new PngEncoder + { + ColorType = PngColorType.Rgb, + }); + + image.Dispose(); + + Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); + } + }); + } + else + { + Logger.Error?.Print(LogClass.Application, $"Screenshot is empty. Size : {e.Data.Length} bytes. Resolution : {e.Width}x{e.Height}", "Screenshot"); + } + } + + public void Start() + { + if (OperatingSystem.IsWindows()) + { + _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); + } + + DisplaySleep.Prevent(); + + NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); + TouchScreenManager.Initialize(Device); + + _viewModel.IsGameRunning = true; + + Dispatcher.UIThread.InvokeAsync(() => + { + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version); + }); + + _viewModel.SetUiProgressHandlers(Device); + + RendererHost.BoundsChanged += Window_BoundsChanged; + + _isActive = true; + + _renderingThread.Start(); + + _viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value; + + MainLoop(); + + Exit(); + } + + private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args) + { + if (Device != null) + { + Device.Configuration.IgnoreMissingServices = args.NewValue; + } + } + + private void UpdateAspectRatioState(object sender, ReactiveEventArgs args) + { + if (Device != null) + { + Device.Configuration.AspectRatio = args.NewValue; + } + } + + private void UpdateAntiAliasing(object sender, ReactiveEventArgs e) + { + _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); + } + + private void UpdateDockedModeState(object sender, ReactiveEventArgs e) + { + Device?.System.ChangeDockedModeState(e.NewValue); + } + + private void UpdateAudioVolumeState(object sender, ReactiveEventArgs e) + { + Device?.SetVolume(e.NewValue); + + Dispatcher.UIThread.Post(() => + { + _viewModel.Volume = e.NewValue; + }); + } + + private void UpdateEnableInternetAccessState(object sender, ReactiveEventArgs e) + { + Device.Configuration.EnableInternetAccess = e.NewValue; + } + + private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs e) + { + Device.Configuration.MultiplayerLanInterfaceId = e.NewValue; + } + + private void UpdateMultiplayerModeState(object sender, ReactiveEventArgs e) + { + Device.Configuration.MultiplayerMode = e.NewValue; + } + + public void ToggleVSync() + { + Device.EnableDeviceVsync = !Device.EnableDeviceVsync; + _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + } + + public void Stop() + { + _isActive = false; + } + + private void Exit() + { + (_keyboardInterface as AvaloniaKeyboard)?.Clear(); + + if (_isStopped) + { + return; + } + + _isStopped = true; + _isActive = false; + } + + public void DisposeContext() + { + Dispose(); + + _isActive = false; + + // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. + // We only need to wait for all commands submitted during the main gpu loop to be processed. + _gpuDoneEvent.WaitOne(); + _gpuDoneEvent.Dispose(); + + DisplaySleep.Restore(); + + NpadManager.Dispose(); + TouchScreenManager.Dispose(); + Device.Dispose(); + + DisposeGpu(); + + AppExit?.Invoke(this, EventArgs.Empty); + } + + private void Dispose() + { + if (Device.Processes != null) + { + MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); + } + + ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; + ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; + ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; + ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough; + + _topLevel.PointerMoved -= TopLevel_PointerEnteredOrMoved; + _topLevel.PointerEntered -= TopLevel_PointerEnteredOrMoved; + _topLevel.PointerExited -= TopLevel_PointerExited; + + _gpuCancellationTokenSource.Cancel(); + _gpuCancellationTokenSource.Dispose(); + + _chrono.Stop(); + } + + public void DisposeGpu() + { + if (OperatingSystem.IsWindows()) + { + _windowsMultimediaTimerResolution?.Dispose(); + _windowsMultimediaTimerResolution = null; + } + + if (RendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow) + { + // Try to bind the OpenGL context before calling the shutdown event. + openGlWindow.MakeCurrent(false, false); + + Device.DisposeGpu(); + + // Unbind context and destroy everything. + openGlWindow.MakeCurrent(true, false); + } + else + { + Device.DisposeGpu(); + } + } + + private void HideCursorState_Changed(object sender, ReactiveEventArgs state) + { + if (state.NewValue == HideCursorMode.OnIdle) + { + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + } + } + + public async Task LoadGuestApplication() + { + InitializeSwitchInstance(); + MainWindow.UpdateGraphicsConfig(); + + SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); + + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) + { + { + if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) + { + if (userError == UserError.NoFirmware) + { + UserResult result = await ContentDialogHelper.CreateConfirmationDialog( + LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + ""); + + if (result != UserResult.Yes) + { + await UserErrorDialog.ShowUserErrorDialog(userError); + Device.Dispose(); + + return false; + } + } + + if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) + { + await UserErrorDialog.ShowUserErrorDialog(userError); + Device.Dispose(); + + return false; + } + + // Tell the user that we installed a firmware for them. + if (userError == UserError.NoFirmware) + { + firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); + + _viewModel.RefreshFirmwareStatus(); + + await ContentDialogHelper.CreateInfoDialog( + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogOk], + "", + LocaleManager.Instance[LocaleKeys.RyujinxInfo]); + } + } + else + { + await UserErrorDialog.ShowUserErrorDialog(userError); + Device.Dispose(); + + return false; + } + } + } + } + + Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); + + if (_isFirmwareTitle) + { + Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); + + if (!Device.LoadNca(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + } + else if (Directory.Exists(ApplicationPath)) + { + string[] romFsFiles = Directory.GetFiles(ApplicationPath, "*.istorage"); + + if (romFsFiles.Length == 0) + { + romFsFiles = Directory.GetFiles(ApplicationPath, "*.romfs"); + } + + if (romFsFiles.Length > 0) + { + Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); + + if (!Device.LoadCart(ApplicationPath, romFsFiles[0])) + { + Device.Dispose(); + + return false; + } + } + else + { + Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); + + if (!Device.LoadCart(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + } + } + else if (File.Exists(ApplicationPath)) + { + switch (Path.GetExtension(ApplicationPath).ToLowerInvariant()) + { + case ".xci": + { + Logger.Info?.Print(LogClass.Application, "Loading as XCI."); + + if (!Device.LoadXci(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + + break; + } + case ".nca": + { + Logger.Info?.Print(LogClass.Application, "Loading as NCA."); + + if (!Device.LoadNca(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + + break; + } + case ".nsp": + case ".pfs0": + { + Logger.Info?.Print(LogClass.Application, "Loading as NSP."); + + if (!Device.LoadNsp(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + + break; + } + default: + { + Logger.Info?.Print(LogClass.Application, "Loading as homebrew."); + + try + { + if (!Device.LoadProgram(ApplicationPath)) + { + Device.Dispose(); + + return false; + } + } + catch (ArgumentOutOfRangeException) + { + Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); + + Device.Dispose(); + + return false; + } + + break; + } + } + } + else + { + Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); + + Device.Dispose(); + + return false; + } + + DiscordIntegrationModule.SwitchToPlayingState(Device.Processes.ActiveApplication.ProgramIdText, Device.Processes.ActiveApplication.Name); + + ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => + { + appMetadata.UpdatePreGame(); + }); + + return true; + } + + internal void Resume() + { + Device?.System.TogglePauseEmulation(false); + + _viewModel.IsPaused = false; + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version); + Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); + } + + internal void Pause() + { + Device?.System.TogglePauseEmulation(true); + + _viewModel.IsPaused = true; + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, LocaleManager.Instance[LocaleKeys.Paused]); + Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); + } + + private void InitializeSwitchInstance() + { + // Initialize KeySet. + VirtualFileSystem.ReloadKeySet(); + + // Initialize Renderer. + IRenderer renderer; + + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) + { + renderer = new VulkanRenderer( + Vk.GetApi(), + (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, + VulkanHelper.GetRequiredInstanceExtensions, + ConfigurationState.Instance.Graphics.PreferredGpu.Value); + } +<<<<<<< HEAD +======= + else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal && OperatingSystem.IsMacOS()) + { + renderer = new MetalRenderer(); + } +>>>>>>> b83dc41f8 (Formatting) + else + { + renderer = new OpenGLRenderer(); + } + + BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; + + var isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); + if (isGALThreaded) + { + renderer = new ThreadedRenderer(renderer); + } + + Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}"); + + // Initialize Configuration. + var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB; + + HLEConfiguration configuration = new(VirtualFileSystem, + _viewModel.LibHacHorizonManager, + ContentManager, + _accountManager, + _userChannelPersistence, + renderer, + InitializeAudio(), + memoryConfiguration, + _viewModel.UiHandler, + (SystemLanguage)ConfigurationState.Instance.System.Language.Value, + (RegionCode)ConfigurationState.Instance.System.Region.Value, + ConfigurationState.Instance.Graphics.EnableVsync, + ConfigurationState.Instance.System.EnableDockedMode, + ConfigurationState.Instance.System.EnablePtc, + ConfigurationState.Instance.System.EnableInternetAccess, + ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, + ConfigurationState.Instance.System.FsGlobalAccessLogMode, + ConfigurationState.Instance.System.SystemTimeOffset, + ConfigurationState.Instance.System.TimeZone, + ConfigurationState.Instance.System.MemoryManagerMode, + ConfigurationState.Instance.System.IgnoreMissingServices, + ConfigurationState.Instance.Graphics.AspectRatio, + ConfigurationState.Instance.System.AudioVolume, + ConfigurationState.Instance.System.UseHypervisor, + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, + ConfigurationState.Instance.Multiplayer.Mode); + + Device = new Switch(configuration); + } + + private static IHardwareDeviceDriver InitializeAudio() + { + var availableBackends = new List + { + AudioBackend.SDL2, + AudioBackend.SoundIo, + AudioBackend.OpenAl, + AudioBackend.Dummy, + }; + + AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value; + + for (int i = 0; i < availableBackends.Count; i++) + { + if (availableBackends[i] == preferredBackend) + { + availableBackends.RemoveAt(i); + availableBackends.Insert(0, preferredBackend); + break; + } + } + + static IHardwareDeviceDriver InitializeAudioBackend(AudioBackend backend, AudioBackend nextBackend) where T : IHardwareDeviceDriver, new() + { + if (T.IsSupported) + { + return new T(); + } + + Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}."); + + return null; + } + + IHardwareDeviceDriver deviceDriver = null; + + for (int i = 0; i < availableBackends.Count; i++) + { + AudioBackend currentBackend = availableBackends[i]; + AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy; + + deviceDriver = currentBackend switch + { + AudioBackend.SDL2 => InitializeAudioBackend(AudioBackend.SDL2, nextBackend), + AudioBackend.SoundIo => InitializeAudioBackend(AudioBackend.SoundIo, nextBackend), + AudioBackend.OpenAl => InitializeAudioBackend(AudioBackend.OpenAl, nextBackend), + _ => new DummyHardwareDeviceDriver(), + }; + + if (deviceDriver != null) + { + ConfigurationState.Instance.System.AudioBackend.Value = currentBackend; + break; + } + } + + MainWindowViewModel.SaveConfig(); + + return deviceDriver; + } + + private void Window_BoundsChanged(object sender, Size e) + { + Width = (int)e.Width; + Height = (int)e.Height; + + SetRendererWindowSize(e); + } + + private void MainLoop() + { + while (_isActive) + { + UpdateFrame(); + + // Polling becomes expensive if it's not slept. + Thread.Sleep(1); + } + } + + private void RenderLoop() + { + Dispatcher.UIThread.InvokeAsync(() => + { + if (_viewModel.StartGamesInFullscreen) + { + _viewModel.WindowState = WindowState.FullScreen; + } + + if (_viewModel.WindowState == WindowState.FullScreen) + { + _viewModel.ShowMenuAndStatusBar = false; + } + }); + + _renderer = Device.Gpu.Renderer is ThreadedRenderer tr ? tr.BaseRenderer : Device.Gpu.Renderer; + + _renderer.ScreenCaptured += Renderer_ScreenCaptured; + + (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer); + + Device.Gpu.Renderer.Initialize(_glLogLevel); + + _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); + _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + _renderer?.Window?.SetColorSpacePassthrough(ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); + + Width = (int)RendererHost.Bounds.Width; + Height = (int)RendererHost.Bounds.Height; + + _renderer.Window.SetSize((int)(Width * _topLevel.RenderScaling), (int)(Height * _topLevel.RenderScaling)); + + _chrono.Start(); + + Device.Gpu.Renderer.RunLoop(() => + { + Device.Gpu.SetGpuThread(); + Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); + + _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + + while (_isActive) + { + _ticks += _chrono.ElapsedTicks; + + _chrono.Restart(); + + if (Device.WaitFifo()) + { + Device.Statistics.RecordFifoStart(); + Device.ProcessFrame(); + Device.Statistics.RecordFifoEnd(); + } + + while (Device.ConsumeFrameAvailable()) + { + if (!_renderingStarted) + { + _renderingStarted = true; + _viewModel.SwitchToRenderer(false); + InitStatus(); + } + + Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); + } + + if (_ticks >= _ticksPerFrame) + { + UpdateStatus(); + } + } + + // Make sure all commands in the run loop are fully executed before leaving the loop. + if (Device.Gpu.Renderer is ThreadedRenderer threaded) + { + threaded.FlushThreadedCommands(); + } + + _gpuDoneEvent.Set(); + }); + + (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); + } + + public void InitStatus() + { + StatusInitEvent?.Invoke(this, new StatusInitEventArgs( + ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch + { + GraphicsBackend.Vulkan => "Vulkan", + GraphicsBackend.OpenGl => "OpenGL", + _ => throw new NotImplementedException() + }, + $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); + } + + public void UpdateStatus() + { + // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. + string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; + + if (GraphicsConfig.ResScale != 1) + { + dockedMode += $" ({GraphicsConfig.ResScale}x)"; + } + + StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( + Device.EnableDeviceVsync, + LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", + ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToText(), + dockedMode, + ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), + LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", + $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", + $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); + } + + public async Task ShowExitPrompt() + { + bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit; + if (!shouldExit) + { + if (_dialogShown) + { + return; + } + + _dialogShown = true; + + shouldExit = await ContentDialogHelper.CreateStopEmulationDialog(); + + _dialogShown = false; + } + + if (shouldExit) + { + Stop(); + } + } + + private bool UpdateFrame() + { + if (!_isActive) + { + return false; + } + + NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); + + if (_viewModel.IsActive) + { + if (_isCursorInRenderer) + { + if (ConfigurationState.Instance.Hid.EnableMouse) + { + HideCursor(); + } + else + { + switch (ConfigurationState.Instance.HideCursor.Value) + { + case HideCursorMode.Never: + ShowCursor(); + break; + case HideCursorMode.OnIdle: + if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency) + { + HideCursor(); + } + else + { + ShowCursor(); + } + break; + case HideCursorMode.Always: + HideCursor(); + break; + } + } + } + else + { + ShowCursor(); + } + + Dispatcher.UIThread.Post(() => + { + if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen) + { + Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); + } + }); + + KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); + + if (currentHotkeyState != _prevHotkeyState) + { + switch (currentHotkeyState) + { + case KeyboardHotkeyState.ToggleVSync: + ToggleVSync(); + break; + case KeyboardHotkeyState.Screenshot: + ScreenshotRequested = true; + break; + case KeyboardHotkeyState.ShowUI: + _viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar; + break; + case KeyboardHotkeyState.Pause: + if (_viewModel.IsPaused) + { + Resume(); + } + else + { + Pause(); + } + break; + case KeyboardHotkeyState.ToggleMute: + if (Device.IsAudioMuted()) + { + Device.SetVolume(_viewModel.VolumeBeforeMute); + } + else + { + _viewModel.VolumeBeforeMute = Device.GetVolume(); + Device.SetVolume(0); + } + + _viewModel.Volume = Device.GetVolume(); + break; + case KeyboardHotkeyState.ResScaleUp: + GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; + break; + case KeyboardHotkeyState.ResScaleDown: + GraphicsConfig.ResScale = + (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; + break; + case KeyboardHotkeyState.VolumeUp: + _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); + Device.SetVolume(_newVolume); + + _viewModel.Volume = Device.GetVolume(); + break; + case KeyboardHotkeyState.VolumeDown: + _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); + Device.SetVolume(_newVolume); + + _viewModel.Volume = Device.GetVolume(); + break; + case KeyboardHotkeyState.None: + (_keyboardInterface as AvaloniaKeyboard).Clear(); + break; + } + } + + _prevHotkeyState = currentHotkeyState; + + if (ScreenshotRequested) + { + ScreenshotRequested = false; + _renderer.Screenshot(); + } + } + + // Touchscreen. + bool hasTouch = false; + + if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) + { + hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); + } + + if (!hasTouch) + { + Device.Hid.Touchscreen.Update(); + } + + Device.Hid.DebugPad.Update(); + + return true; + } + + private KeyboardHotkeyState GetHotkeyState() + { + KeyboardHotkeyState state = KeyboardHotkeyState.None; + + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + { + state = KeyboardHotkeyState.ToggleVSync; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) + { + state = KeyboardHotkeyState.Screenshot; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI)) + { + state = KeyboardHotkeyState.ShowUI; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) + { + state = KeyboardHotkeyState.Pause; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) + { + state = KeyboardHotkeyState.ToggleMute; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) + { + state = KeyboardHotkeyState.ResScaleUp; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) + { + state = KeyboardHotkeyState.ResScaleDown; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) + { + state = KeyboardHotkeyState.VolumeUp; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) + { + state = KeyboardHotkeyState.VolumeDown; + } + + return state; + } + } +} -- 2.47.1 From 2812f016433088dacbfed8b677086a4593112b1c Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 20:58:17 -0600 Subject: [PATCH 026/368] merge leftovers --- src/Ryujinx/AppHost.cs.orig | 1226 ----------------------------------- 1 file changed, 1226 deletions(-) delete mode 100644 src/Ryujinx/AppHost.cs.orig diff --git a/src/Ryujinx/AppHost.cs.orig b/src/Ryujinx/AppHost.cs.orig deleted file mode 100644 index 302ef914c..000000000 --- a/src/Ryujinx/AppHost.cs.orig +++ /dev/null @@ -1,1226 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Input; -using Avalonia.Threading; -using LibHac.Tools.FsSystem; -using Ryujinx.Audio.Backends.Dummy; -using Ryujinx.Audio.Backends.OpenAL; -using Ryujinx.Audio.Backends.SDL2; -using Ryujinx.Audio.Backends.SoundIo; -using Ryujinx.Audio.Integration; -using Ryujinx.Ava.Common; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Input; -using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.UI.Renderer; -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Ava.UI.Windows; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Multiplayer; -using Ryujinx.Common.Logging; -using Ryujinx.Common.SystemInterop; -using Ryujinx.Common.Utilities; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.Graphics.Gpu; -using Ryujinx.Graphics.OpenGL; -using Ryujinx.Graphics.Vulkan; -using Ryujinx.Graphics.Metal; -using Ryujinx.HLE; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.Input; -using Ryujinx.Input.HLE; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Silk.NET.Vulkan; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SPB.Graphics.Vulkan; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; -using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; -using Image = SixLabors.ImageSharp.Image; -using InputManager = Ryujinx.Input.HLE.InputManager; -using IRenderer = Ryujinx.Graphics.GAL.IRenderer; -using Key = Ryujinx.Input.Key; -using MouseButton = Ryujinx.Input.MouseButton; -using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; -using Size = Avalonia.Size; -using Switch = Ryujinx.HLE.Switch; - -namespace Ryujinx.Ava -{ - internal class AppHost - { - private const int CursorHideIdleTime = 5; // Hide Cursor seconds. - private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. - private const int TargetFps = 60; - private const float VolumeDelta = 0.05f; - - private static readonly Cursor _invisibleCursor = new(StandardCursorType.None); - private readonly IntPtr _invisibleCursorWin; - private readonly IntPtr _defaultCursorWin; - - private readonly long _ticksPerFrame; - private readonly Stopwatch _chrono; - private long _ticks; - - private readonly AccountManager _accountManager; - private readonly UserChannelPersistence _userChannelPersistence; - private readonly InputManager _inputManager; - - private readonly MainWindowViewModel _viewModel; - private readonly IKeyboard _keyboardInterface; - private readonly TopLevel _topLevel; - public RendererHost RendererHost; - - private readonly GraphicsDebugLevel _glLogLevel; - private float _newVolume; - private KeyboardHotkeyState _prevHotkeyState; - - private long _lastCursorMoveTime; - private bool _isCursorInRenderer = true; - - private bool _isStopped; - private bool _isActive; - private bool _renderingStarted; - - private readonly ManualResetEvent _gpuDoneEvent; - - private IRenderer _renderer; - private readonly Thread _renderingThread; - private readonly CancellationTokenSource _gpuCancellationTokenSource; - private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; - - private bool _dialogShown; - private readonly bool _isFirmwareTitle; - - private readonly object _lockObject = new(); - - public event EventHandler AppExit; - public event EventHandler StatusInitEvent; - public event EventHandler StatusUpdatedEvent; - - public VirtualFileSystem VirtualFileSystem { get; } - public ContentManager ContentManager { get; } - public NpadManager NpadManager { get; } - public TouchScreenManager TouchScreenManager { get; } - public Switch Device { get; set; } - - public int Width { get; private set; } - public int Height { get; private set; } - public string ApplicationPath { get; private set; } - public bool ScreenshotRequested { get; set; } - - public AppHost( - RendererHost renderer, - InputManager inputManager, - string applicationPath, - VirtualFileSystem virtualFileSystem, - ContentManager contentManager, - AccountManager accountManager, - UserChannelPersistence userChannelPersistence, - MainWindowViewModel viewmodel, - TopLevel topLevel) - { - _viewModel = viewmodel; - _inputManager = inputManager; - _accountManager = accountManager; - _userChannelPersistence = userChannelPersistence; - _renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" }; - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; - _topLevel = topLevel; - - _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer)); - - _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); - - NpadManager = _inputManager.CreateNpadManager(); - TouchScreenManager = _inputManager.CreateTouchScreenManager(); - ApplicationPath = applicationPath; - VirtualFileSystem = virtualFileSystem; - ContentManager = contentManager; - - RendererHost = renderer; - - _chrono = new Stopwatch(); - _ticksPerFrame = Stopwatch.Frequency / TargetFps; - - if (ApplicationPath.StartsWith("@SystemContent")) - { - ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); - - _isFirmwareTitle = true; - } - - ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; - - _topLevel.PointerMoved += TopLevel_PointerEnteredOrMoved; - _topLevel.PointerEntered += TopLevel_PointerEnteredOrMoved; - _topLevel.PointerExited += TopLevel_PointerExited; - - if (OperatingSystem.IsWindows()) - { - _invisibleCursorWin = CreateEmptyCursor(); - _defaultCursorWin = CreateArrowCursor(); - } - - ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; - ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; - ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; - ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough; - - ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState; - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; - ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState; - - _gpuCancellationTokenSource = new CancellationTokenSource(); - _gpuDoneEvent = new ManualResetEvent(false); - } - - private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e) - { - if (sender is MainWindow window) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - - var point = e.GetCurrentPoint(window).Position; - var bounds = RendererHost.EmbeddedWindow.Bounds; - - _isCursorInRenderer = point.X >= bounds.X && - point.X <= bounds.Width + bounds.X && - point.Y >= bounds.Y && - point.Y <= bounds.Height + bounds.Y; - } - } - - private void TopLevel_PointerExited(object sender, PointerEventArgs e) - { - _isCursorInRenderer = false; - } - - private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs e) - { - _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - } - - private void UpdateScalingFilter(object sender, ReactiveEventArgs e) - { - _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - } - - private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs e) - { - _renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); - } - - private void ShowCursor() - { - Dispatcher.UIThread.Post(() => - { - _viewModel.Cursor = Cursor.Default; - - if (OperatingSystem.IsWindows()) - { - SetCursor(_defaultCursorWin); - } - }); - } - - private void HideCursor() - { - Dispatcher.UIThread.Post(() => - { - _viewModel.Cursor = _invisibleCursor; - - if (OperatingSystem.IsWindows()) - { - SetCursor(_invisibleCursorWin); - } - }); - } - - private void SetRendererWindowSize(Size size) - { - if (_renderer != null) - { - double scale = _topLevel.RenderScaling; - - _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); - } - } - - private void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e) - { - if (e.Data.Length > 0 && e.Height > 0 && e.Width > 0) - { - Task.Run(() => - { - lock (_lockObject) - { - string applicationName = Device.Processes.ActiveApplication.Name; - string sanitizedApplicationName = FileSystemUtils.SanitizeFileName(applicationName); - DateTime currentTime = DateTime.Now; - - string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; - - string directory = AppDataManager.Mode switch - { - AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), - _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), - }; - - string path = Path.Combine(directory, filename); - - try - { - Directory.CreateDirectory(directory); - } - catch (Exception ex) - { - Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {directory}. Error : {ex.GetType().Name}", "Screenshot"); - - return; - } - - Image image = e.IsBgra ? Image.LoadPixelData(e.Data, e.Width, e.Height) - : Image.LoadPixelData(e.Data, e.Width, e.Height); - - if (e.FlipX) - { - image.Mutate(x => x.Flip(FlipMode.Horizontal)); - } - - if (e.FlipY) - { - image.Mutate(x => x.Flip(FlipMode.Vertical)); - } - - image.SaveAsPng(path, new PngEncoder - { - ColorType = PngColorType.Rgb, - }); - - image.Dispose(); - - Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); - } - }); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Screenshot is empty. Size : {e.Data.Length} bytes. Resolution : {e.Width}x{e.Height}", "Screenshot"); - } - } - - public void Start() - { - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); - } - - DisplaySleep.Prevent(); - - NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); - TouchScreenManager.Initialize(Device); - - _viewModel.IsGameRunning = true; - - Dispatcher.UIThread.InvokeAsync(() => - { - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version); - }); - - _viewModel.SetUiProgressHandlers(Device); - - RendererHost.BoundsChanged += Window_BoundsChanged; - - _isActive = true; - - _renderingThread.Start(); - - _viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value; - - MainLoop(); - - Exit(); - } - - private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args) - { - if (Device != null) - { - Device.Configuration.IgnoreMissingServices = args.NewValue; - } - } - - private void UpdateAspectRatioState(object sender, ReactiveEventArgs args) - { - if (Device != null) - { - Device.Configuration.AspectRatio = args.NewValue; - } - } - - private void UpdateAntiAliasing(object sender, ReactiveEventArgs e) - { - _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); - } - - private void UpdateDockedModeState(object sender, ReactiveEventArgs e) - { - Device?.System.ChangeDockedModeState(e.NewValue); - } - - private void UpdateAudioVolumeState(object sender, ReactiveEventArgs e) - { - Device?.SetVolume(e.NewValue); - - Dispatcher.UIThread.Post(() => - { - _viewModel.Volume = e.NewValue; - }); - } - - private void UpdateEnableInternetAccessState(object sender, ReactiveEventArgs e) - { - Device.Configuration.EnableInternetAccess = e.NewValue; - } - - private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs e) - { - Device.Configuration.MultiplayerLanInterfaceId = e.NewValue; - } - - private void UpdateMultiplayerModeState(object sender, ReactiveEventArgs e) - { - Device.Configuration.MultiplayerMode = e.NewValue; - } - - public void ToggleVSync() - { - Device.EnableDeviceVsync = !Device.EnableDeviceVsync; - _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); - } - - public void Stop() - { - _isActive = false; - } - - private void Exit() - { - (_keyboardInterface as AvaloniaKeyboard)?.Clear(); - - if (_isStopped) - { - return; - } - - _isStopped = true; - _isActive = false; - } - - public void DisposeContext() - { - Dispose(); - - _isActive = false; - - // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. - // We only need to wait for all commands submitted during the main gpu loop to be processed. - _gpuDoneEvent.WaitOne(); - _gpuDoneEvent.Dispose(); - - DisplaySleep.Restore(); - - NpadManager.Dispose(); - TouchScreenManager.Dispose(); - Device.Dispose(); - - DisposeGpu(); - - AppExit?.Invoke(this, EventArgs.Empty); - } - - private void Dispose() - { - if (Device.Processes != null) - { - MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); - } - - ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; - ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; - ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; - ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough; - - _topLevel.PointerMoved -= TopLevel_PointerEnteredOrMoved; - _topLevel.PointerEntered -= TopLevel_PointerEnteredOrMoved; - _topLevel.PointerExited -= TopLevel_PointerExited; - - _gpuCancellationTokenSource.Cancel(); - _gpuCancellationTokenSource.Dispose(); - - _chrono.Stop(); - } - - public void DisposeGpu() - { - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution?.Dispose(); - _windowsMultimediaTimerResolution = null; - } - - if (RendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow) - { - // Try to bind the OpenGL context before calling the shutdown event. - openGlWindow.MakeCurrent(false, false); - - Device.DisposeGpu(); - - // Unbind context and destroy everything. - openGlWindow.MakeCurrent(true, false); - } - else - { - Device.DisposeGpu(); - } - } - - private void HideCursorState_Changed(object sender, ReactiveEventArgs state) - { - if (state.NewValue == HideCursorMode.OnIdle) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - } - } - - public async Task LoadGuestApplication() - { - InitializeSwitchInstance(); - MainWindow.UpdateGraphicsConfig(); - - SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) - { - { - if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) - { - if (userError == UserError.NoFirmware) - { - UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString), - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - ""); - - if (result != UserResult.Yes) - { - await UserErrorDialog.ShowUserErrorDialog(userError); - Device.Dispose(); - - return false; - } - } - - if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) - { - await UserErrorDialog.ShowUserErrorDialog(userError); - Device.Dispose(); - - return false; - } - - // Tell the user that we installed a firmware for them. - if (userError == UserError.NoFirmware) - { - firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - - _viewModel.RefreshFirmwareStatus(); - - await ContentDialogHelper.CreateInfoDialog( - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString), - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString), - LocaleManager.Instance[LocaleKeys.InputDialogOk], - "", - LocaleManager.Instance[LocaleKeys.RyujinxInfo]); - } - } - else - { - await UserErrorDialog.ShowUserErrorDialog(userError); - Device.Dispose(); - - return false; - } - } - } - } - - Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); - - if (_isFirmwareTitle) - { - Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); - - if (!Device.LoadNca(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - } - else if (Directory.Exists(ApplicationPath)) - { - string[] romFsFiles = Directory.GetFiles(ApplicationPath, "*.istorage"); - - if (romFsFiles.Length == 0) - { - romFsFiles = Directory.GetFiles(ApplicationPath, "*.romfs"); - } - - if (romFsFiles.Length > 0) - { - Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); - - if (!Device.LoadCart(ApplicationPath, romFsFiles[0])) - { - Device.Dispose(); - - return false; - } - } - else - { - Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); - - if (!Device.LoadCart(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - } - } - else if (File.Exists(ApplicationPath)) - { - switch (Path.GetExtension(ApplicationPath).ToLowerInvariant()) - { - case ".xci": - { - Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - - if (!Device.LoadXci(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - - break; - } - case ".nca": - { - Logger.Info?.Print(LogClass.Application, "Loading as NCA."); - - if (!Device.LoadNca(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - - break; - } - case ".nsp": - case ".pfs0": - { - Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - - if (!Device.LoadNsp(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - - break; - } - default: - { - Logger.Info?.Print(LogClass.Application, "Loading as homebrew."); - - try - { - if (!Device.LoadProgram(ApplicationPath)) - { - Device.Dispose(); - - return false; - } - } - catch (ArgumentOutOfRangeException) - { - Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); - - Device.Dispose(); - - return false; - } - - break; - } - } - } - else - { - Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); - - Device.Dispose(); - - return false; - } - - DiscordIntegrationModule.SwitchToPlayingState(Device.Processes.ActiveApplication.ProgramIdText, Device.Processes.ActiveApplication.Name); - - ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => - { - appMetadata.UpdatePreGame(); - }); - - return true; - } - - internal void Resume() - { - Device?.System.TogglePauseEmulation(false); - - _viewModel.IsPaused = false; - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version); - Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); - } - - internal void Pause() - { - Device?.System.TogglePauseEmulation(true); - - _viewModel.IsPaused = true; - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, LocaleManager.Instance[LocaleKeys.Paused]); - Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); - } - - private void InitializeSwitchInstance() - { - // Initialize KeySet. - VirtualFileSystem.ReloadKeySet(); - - // Initialize Renderer. - IRenderer renderer; - - if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) - { - renderer = new VulkanRenderer( - Vk.GetApi(), - (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, - VulkanHelper.GetRequiredInstanceExtensions, - ConfigurationState.Instance.Graphics.PreferredGpu.Value); - } -<<<<<<< HEAD -======= - else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal && OperatingSystem.IsMacOS()) - { - renderer = new MetalRenderer(); - } ->>>>>>> b83dc41f8 (Formatting) - else - { - renderer = new OpenGLRenderer(); - } - - BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - - var isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - if (isGALThreaded) - { - renderer = new ThreadedRenderer(renderer); - } - - Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}"); - - // Initialize Configuration. - var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB; - - HLEConfiguration configuration = new(VirtualFileSystem, - _viewModel.LibHacHorizonManager, - ContentManager, - _accountManager, - _userChannelPersistence, - renderer, - InitializeAudio(), - memoryConfiguration, - _viewModel.UiHandler, - (SystemLanguage)ConfigurationState.Instance.System.Language.Value, - (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.EnableVsync, - ConfigurationState.Instance.System.EnableDockedMode, - ConfigurationState.Instance.System.EnablePtc, - ConfigurationState.Instance.System.EnableInternetAccess, - ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, - ConfigurationState.Instance.System.FsGlobalAccessLogMode, - ConfigurationState.Instance.System.SystemTimeOffset, - ConfigurationState.Instance.System.TimeZone, - ConfigurationState.Instance.System.MemoryManagerMode, - ConfigurationState.Instance.System.IgnoreMissingServices, - ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume, - ConfigurationState.Instance.System.UseHypervisor, - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, - ConfigurationState.Instance.Multiplayer.Mode); - - Device = new Switch(configuration); - } - - private static IHardwareDeviceDriver InitializeAudio() - { - var availableBackends = new List - { - AudioBackend.SDL2, - AudioBackend.SoundIo, - AudioBackend.OpenAl, - AudioBackend.Dummy, - }; - - AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value; - - for (int i = 0; i < availableBackends.Count; i++) - { - if (availableBackends[i] == preferredBackend) - { - availableBackends.RemoveAt(i); - availableBackends.Insert(0, preferredBackend); - break; - } - } - - static IHardwareDeviceDriver InitializeAudioBackend(AudioBackend backend, AudioBackend nextBackend) where T : IHardwareDeviceDriver, new() - { - if (T.IsSupported) - { - return new T(); - } - - Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}."); - - return null; - } - - IHardwareDeviceDriver deviceDriver = null; - - for (int i = 0; i < availableBackends.Count; i++) - { - AudioBackend currentBackend = availableBackends[i]; - AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy; - - deviceDriver = currentBackend switch - { - AudioBackend.SDL2 => InitializeAudioBackend(AudioBackend.SDL2, nextBackend), - AudioBackend.SoundIo => InitializeAudioBackend(AudioBackend.SoundIo, nextBackend), - AudioBackend.OpenAl => InitializeAudioBackend(AudioBackend.OpenAl, nextBackend), - _ => new DummyHardwareDeviceDriver(), - }; - - if (deviceDriver != null) - { - ConfigurationState.Instance.System.AudioBackend.Value = currentBackend; - break; - } - } - - MainWindowViewModel.SaveConfig(); - - return deviceDriver; - } - - private void Window_BoundsChanged(object sender, Size e) - { - Width = (int)e.Width; - Height = (int)e.Height; - - SetRendererWindowSize(e); - } - - private void MainLoop() - { - while (_isActive) - { - UpdateFrame(); - - // Polling becomes expensive if it's not slept. - Thread.Sleep(1); - } - } - - private void RenderLoop() - { - Dispatcher.UIThread.InvokeAsync(() => - { - if (_viewModel.StartGamesInFullscreen) - { - _viewModel.WindowState = WindowState.FullScreen; - } - - if (_viewModel.WindowState == WindowState.FullScreen) - { - _viewModel.ShowMenuAndStatusBar = false; - } - }); - - _renderer = Device.Gpu.Renderer is ThreadedRenderer tr ? tr.BaseRenderer : Device.Gpu.Renderer; - - _renderer.ScreenCaptured += Renderer_ScreenCaptured; - - (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer); - - Device.Gpu.Renderer.Initialize(_glLogLevel); - - _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); - _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - _renderer?.Window?.SetColorSpacePassthrough(ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); - - Width = (int)RendererHost.Bounds.Width; - Height = (int)RendererHost.Bounds.Height; - - _renderer.Window.SetSize((int)(Width * _topLevel.RenderScaling), (int)(Height * _topLevel.RenderScaling)); - - _chrono.Start(); - - Device.Gpu.Renderer.RunLoop(() => - { - Device.Gpu.SetGpuThread(); - Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); - - _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); - - while (_isActive) - { - _ticks += _chrono.ElapsedTicks; - - _chrono.Restart(); - - if (Device.WaitFifo()) - { - Device.Statistics.RecordFifoStart(); - Device.ProcessFrame(); - Device.Statistics.RecordFifoEnd(); - } - - while (Device.ConsumeFrameAvailable()) - { - if (!_renderingStarted) - { - _renderingStarted = true; - _viewModel.SwitchToRenderer(false); - InitStatus(); - } - - Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); - } - - if (_ticks >= _ticksPerFrame) - { - UpdateStatus(); - } - } - - // Make sure all commands in the run loop are fully executed before leaving the loop. - if (Device.Gpu.Renderer is ThreadedRenderer threaded) - { - threaded.FlushThreadedCommands(); - } - - _gpuDoneEvent.Set(); - }); - - (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); - } - - public void InitStatus() - { - StatusInitEvent?.Invoke(this, new StatusInitEventArgs( - ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch - { - GraphicsBackend.Vulkan => "Vulkan", - GraphicsBackend.OpenGl => "OpenGL", - _ => throw new NotImplementedException() - }, - $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); - } - - public void UpdateStatus() - { - // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. - string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; - - if (GraphicsConfig.ResScale != 1) - { - dockedMode += $" ({GraphicsConfig.ResScale}x)"; - } - - StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, - LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", - ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToText(), - dockedMode, - ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", - $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", - $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); - } - - public async Task ShowExitPrompt() - { - bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit; - if (!shouldExit) - { - if (_dialogShown) - { - return; - } - - _dialogShown = true; - - shouldExit = await ContentDialogHelper.CreateStopEmulationDialog(); - - _dialogShown = false; - } - - if (shouldExit) - { - Stop(); - } - } - - private bool UpdateFrame() - { - if (!_isActive) - { - return false; - } - - NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - - if (_viewModel.IsActive) - { - if (_isCursorInRenderer) - { - if (ConfigurationState.Instance.Hid.EnableMouse) - { - HideCursor(); - } - else - { - switch (ConfigurationState.Instance.HideCursor.Value) - { - case HideCursorMode.Never: - ShowCursor(); - break; - case HideCursorMode.OnIdle: - if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency) - { - HideCursor(); - } - else - { - ShowCursor(); - } - break; - case HideCursorMode.Always: - HideCursor(); - break; - } - } - } - else - { - ShowCursor(); - } - - Dispatcher.UIThread.Post(() => - { - if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen) - { - Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); - } - }); - - KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); - - if (currentHotkeyState != _prevHotkeyState) - { - switch (currentHotkeyState) - { - case KeyboardHotkeyState.ToggleVSync: - ToggleVSync(); - break; - case KeyboardHotkeyState.Screenshot: - ScreenshotRequested = true; - break; - case KeyboardHotkeyState.ShowUI: - _viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar; - break; - case KeyboardHotkeyState.Pause: - if (_viewModel.IsPaused) - { - Resume(); - } - else - { - Pause(); - } - break; - case KeyboardHotkeyState.ToggleMute: - if (Device.IsAudioMuted()) - { - Device.SetVolume(_viewModel.VolumeBeforeMute); - } - else - { - _viewModel.VolumeBeforeMute = Device.GetVolume(); - Device.SetVolume(0); - } - - _viewModel.Volume = Device.GetVolume(); - break; - case KeyboardHotkeyState.ResScaleUp: - GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; - break; - case KeyboardHotkeyState.ResScaleDown: - GraphicsConfig.ResScale = - (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; - break; - case KeyboardHotkeyState.VolumeUp: - _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); - Device.SetVolume(_newVolume); - - _viewModel.Volume = Device.GetVolume(); - break; - case KeyboardHotkeyState.VolumeDown: - _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); - Device.SetVolume(_newVolume); - - _viewModel.Volume = Device.GetVolume(); - break; - case KeyboardHotkeyState.None: - (_keyboardInterface as AvaloniaKeyboard).Clear(); - break; - } - } - - _prevHotkeyState = currentHotkeyState; - - if (ScreenshotRequested) - { - ScreenshotRequested = false; - _renderer.Screenshot(); - } - } - - // Touchscreen. - bool hasTouch = false; - - if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) - { - hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - } - - if (!hasTouch) - { - Device.Hid.Touchscreen.Update(); - } - - Device.Hid.DebugPad.Update(); - - return true; - } - - private KeyboardHotkeyState GetHotkeyState() - { - KeyboardHotkeyState state = KeyboardHotkeyState.None; - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) - { - state = KeyboardHotkeyState.ToggleVSync; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) - { - state = KeyboardHotkeyState.Screenshot; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI)) - { - state = KeyboardHotkeyState.ShowUI; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) - { - state = KeyboardHotkeyState.Pause; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) - { - state = KeyboardHotkeyState.ToggleMute; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) - { - state = KeyboardHotkeyState.ResScaleUp; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) - { - state = KeyboardHotkeyState.ResScaleDown; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) - { - state = KeyboardHotkeyState.VolumeUp; - } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) - { - state = KeyboardHotkeyState.VolumeDown; - } - - return state; - } - } -} -- 2.47.1 From c1ef270b9df1eaa303328a8f8ee1f59056bcf850 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 16:47:10 -0400 Subject: [PATCH 027/368] Cleanup encoder getting + Fix capture overflow --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 1 - src/Ryujinx.Graphics.Metal/Pipeline.cs | 111 ++++++++------------ src/Ryujinx.Graphics.Metal/Texture.cs | 73 ++----------- 3 files changed, 51 insertions(+), 134 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 21c8c49b4..8a65c2fcc 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -118,7 +118,6 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateTexture(TextureCreateInfo info) { var texture = new Texture(_device, _pipeline, info); - Logger.Warning?.Print(LogClass.Gpu, "Texture created!"); return texture; } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 6a3d9a801..47426486b 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -20,15 +20,14 @@ namespace Ryujinx.Graphics.Metal private MTLCommandBuffer _commandBuffer; private MTLCommandEncoder _currentEncoder; - public MTLCommandEncoder CurrentEncoder; - private RenderEncoderState _renderEncoderState; private MTLBuffer _indexBuffer; private MTLIndexType _indexType; private ulong _indexBufferOffset; private MTLClearColor _clearColor; - private int frameCount = 0; + private int _frameCount; + private bool _captureEnded = false; public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { @@ -41,6 +40,36 @@ namespace Ryujinx.Graphics.Metal _commandBuffer = _mtlCommandQueue.CommandBuffer(); } + public MTLRenderCommandEncoder GetOrCreateRenderEncoder() + { + if (_currentEncoder is MTLRenderCommandEncoder encoder) + { + return encoder; + } + + return BeginRenderPass(); + } + + public MTLBlitCommandEncoder GetOrCreateBlitEncoder() + { + if (_currentEncoder is MTLBlitCommandEncoder encoder) + { + return encoder; + } + + return BeginBlitPass(); + } + + public MTLComputeCommandEncoder GetOrCreateComputeEncoder() + { + if (_currentEncoder is MTLComputeCommandEncoder encoder) + { + return encoder; + } + + return BeginComputePass(); + } + public void EndCurrentPass() { if (_currentEncoder != null) @@ -48,7 +77,6 @@ namespace Ryujinx.Graphics.Metal _currentEncoder.EndEncoding(); _currentEncoder = null; } - Logger.Warning?.Print(LogClass.Gpu, "Current pass ended"); } public MTLRenderCommandEncoder BeginRenderPass() @@ -58,7 +86,6 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLRenderPassDescriptor(); var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder); - Logger.Warning?.Print(LogClass.Gpu, "Began render pass"); _currentEncoder = renderCommandEncoder; return renderCommandEncoder; @@ -70,7 +97,6 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLBlitPassDescriptor(); var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); - Logger.Warning?.Print(LogClass.Gpu, "Began blit pass"); _currentEncoder = blitCommandEncoder; return blitCommandEncoder; @@ -82,7 +108,6 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _commandBuffer.ComputeCommandEncoder(descriptor); - Logger.Warning?.Print(LogClass.Gpu, "Began compute pass"); _currentEncoder = computeCommandEncoder; return computeCommandEncoder; @@ -103,7 +128,6 @@ namespace Ryujinx.Graphics.Metal descriptor.ColorAttachments.Object(0).ClearColor = _clearColor; var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); - Logger.Warning?.Print(LogClass.Gpu, "Began present"); _renderEncoderState.SetEncoderState(renderCommandEncoder); var sampler = _device.NewSamplerState(new MTLSamplerDescriptor @@ -122,14 +146,16 @@ namespace Ryujinx.Graphics.Metal _commandBuffer.PresentDrawable(drawable); _commandBuffer.Commit(); - Logger.Warning?.Print(LogClass.Gpu, "Presented"); - - frameCount++; - - if (frameCount >= 5) + if (!_captureEnded) { - MTLCaptureManager.SharedCaptureManager().StopCapture(); - Logger.Warning?.Print(LogClass.Gpu, "Trace ended!"); + _frameCount++; + + if (_frameCount >= 5) + { + _captureEnded = true; + MTLCaptureManager.SharedCaptureManager().StopCapture(); + Logger.Warning?.Print(LogClass.Gpu, "Trace ended!"); + } } _commandBuffer = _mtlCommandQueue.CommandBuffer(); @@ -142,16 +168,7 @@ namespace Ryujinx.Graphics.Metal public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) { - MTLBlitCommandEncoder blitCommandEncoder; - - if (_currentEncoder is MTLBlitCommandEncoder encoder) - { - blitCommandEncoder = encoder; - } - else - { - blitCommandEncoder = BeginBlitPass(); - } + var blitCommandEncoder = GetOrCreateBlitEncoder(); // Might need a closer look, range's count, lower, and upper bound // must be a multiple of 4 @@ -183,16 +200,7 @@ namespace Ryujinx.Graphics.Metal public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) { - MTLBlitCommandEncoder blitCommandEncoder; - - if (CurrentEncoder is MTLBlitCommandEncoder encoder) - { - blitCommandEncoder = encoder; - } - else - { - blitCommandEncoder = BeginBlitPass(); - } + var blitCommandEncoder = GetOrCreateBlitEncoder(); MTLBuffer sourceBuffer = new(Unsafe.As(ref source)); MTLBuffer destinationBuffer = new(Unsafe.As(ref destination)); @@ -212,18 +220,7 @@ namespace Ryujinx.Graphics.Metal public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { - Logger.Warning?.Print(LogClass.Gpu, "Draw"); - - MTLRenderCommandEncoder renderCommandEncoder; - - if (_currentEncoder is MTLRenderCommandEncoder encoder) - { - renderCommandEncoder = encoder; - } - else - { - renderCommandEncoder = BeginRenderPass(); - } + var renderCommandEncoder = GetOrCreateRenderEncoder(); // TODO: Support topology re-indexing to provide support for TriangleFans var primitiveType = _renderEncoderState.Topology.Convert(); @@ -233,18 +230,8 @@ namespace Ryujinx.Graphics.Metal public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) { - Logger.Warning?.Print(LogClass.Gpu, "Draw"); + var renderCommandEncoder = GetOrCreateRenderEncoder(); - MTLRenderCommandEncoder renderCommandEncoder; - - if (_currentEncoder is MTLRenderCommandEncoder encoder) - { - renderCommandEncoder = encoder; - } - else - { - renderCommandEncoder = BeginRenderPass(); - } // TODO: Support topology re-indexing to provide support for TriangleFans var primitiveType = _renderEncoderState.Topology.Convert(); @@ -309,8 +296,6 @@ namespace Ryujinx.Graphics.Metal public void SetDepthTest(DepthTestDescriptor depthTest) { - Logger.Warning?.Print(LogClass.Gpu, "Set depth test"); - var depthStencilState = _renderEncoderState.UpdateDepthState( depthTest.TestEnable ? MTLCompareFunction.Always : depthTest.Func.Convert(), depthTest.WriteEnable); @@ -323,8 +308,6 @@ namespace Ryujinx.Graphics.Metal public void SetFaceCulling(bool enable, Face face) { - Logger.Warning?.Print(LogClass.Gpu, "Set face culling"); - var cullMode = enable ? face.Convert() : MTLCullMode.None; if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) @@ -337,8 +320,6 @@ namespace Ryujinx.Graphics.Metal public void SetFrontFace(FrontFace frontFace) { - Logger.Warning?.Print(LogClass.Gpu, "Set front face"); - var winding = frontFace.Convert(); if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) @@ -351,8 +332,6 @@ namespace Ryujinx.Graphics.Metal public void SetIndexBuffer(BufferRange buffer, IndexType type) { - Logger.Warning?.Print(LogClass.Gpu, "Set index buffer"); - if (buffer.Handle != BufferHandle.Null) { _indexType = type.Convert(); diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index ea3a8cba8..21932933a 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -23,7 +23,6 @@ namespace Ryujinx.Graphics.Metal public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) { - Logger.Warning?.Print(LogClass.Gpu, "Texture init"); _device = device; _pipeline = pipeline; _info = info; @@ -53,17 +52,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { - Logger.Warning?.Print(LogClass.Gpu, "Copy to"); - MTLBlitCommandEncoder blitCommandEncoder; - - if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) - { - blitCommandEncoder = encoder; - } - else - { - blitCommandEncoder = _pipeline.BeginBlitPass(); - } + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); if (destination is Texture destinationTexture) { @@ -81,17 +70,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { - Logger.Warning?.Print(LogClass.Gpu, "Copy to"); - MTLBlitCommandEncoder blitCommandEncoder; - - if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) - { - blitCommandEncoder = encoder; - } - else - { - blitCommandEncoder = _pipeline.BeginBlitPass(); - } + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); if (destination is Texture destinationTexture) { @@ -114,17 +93,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(BufferRange range, int layer, int level, int stride) { - Logger.Warning?.Print(LogClass.Gpu, "Copy to"); - MTLBlitCommandEncoder blitCommandEncoder; - - if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) - { - blitCommandEncoder = encoder; - } - else - { - blitCommandEncoder = _pipeline.BeginBlitPass(); - } + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; @@ -166,17 +135,7 @@ namespace Ryujinx.Graphics.Metal // TODO: Handle array formats public unsafe void SetData(SpanOrArray data) { - Logger.Warning?.Print(LogClass.Gpu, "Set data"); - MTLBlitCommandEncoder blitCommandEncoder; - - if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) - { - blitCommandEncoder = encoder; - } - else - { - blitCommandEncoder = _pipeline.BeginBlitPass(); - } + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); var dataSpan = data.Span; var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); @@ -223,17 +182,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data, int layer, int level) { - Logger.Warning?.Print(LogClass.Gpu, "Set data"); - MTLBlitCommandEncoder blitCommandEncoder; - - if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) - { - blitCommandEncoder = encoder; - } - else - { - blitCommandEncoder = _pipeline.BeginBlitPass(); - } + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; @@ -265,17 +214,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { - Logger.Warning?.Print(LogClass.Gpu, "Set data"); - MTLBlitCommandEncoder blitCommandEncoder; - - if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) - { - blitCommandEncoder = encoder; - } - else - { - blitCommandEncoder = _pipeline.BeginBlitPass(); - } + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; -- 2.47.1 From 4cde7a41257d8bf3f9c5ff6c86e7db7a81c7a3f2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 16:53:53 -0400 Subject: [PATCH 028/368] Define MaxFramesPerCapture --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 47426486b..77594c142 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Pipeline : IPipeline, IDisposable { + private const int MaxFramesPerCapture = 50; + private readonly MTLDevice _device; private readonly MTLCommandQueue _mtlCommandQueue; private readonly HelperShaders _helperShaders; @@ -150,7 +152,7 @@ namespace Ryujinx.Graphics.Metal { _frameCount++; - if (_frameCount >= 5) + if (_frameCount >= MaxFramesPerCapture) { _captureEnded = true; MTLCaptureManager.SharedCaptureManager().StopCapture(); -- 2.47.1 From 8b9d6ffc946ad50dd4319a723626024517877b43 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 17:04:59 -0400 Subject: [PATCH 029/368] Easier capture stuff --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 14 -------- src/Ryujinx.Graphics.Metal/Pipeline.cs | 39 +++++++++++++++++---- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 8a65c2fcc..a202db426 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -38,20 +38,6 @@ namespace Ryujinx.Graphics.Metal var layer = _getMetalLayer(); layer.Device = _device; - var captureDescriptor = new MTLCaptureDescriptor - { - CaptureObject = _queue, - Destination = MTLCaptureDestination.GPUTraceDocument, - OutputURL = NSURL.FileURLWithPath(StringHelper.NSString("/Users/isaacmarovitz/Desktop/Trace.gputrace")) - }; - var captureError = new NSError(IntPtr.Zero); - MTLCaptureManager.SharedCaptureManager().StartCapture(captureDescriptor, ref captureError); - if (captureError != IntPtr.Zero) - { - Console.WriteLine($"Failed to start capture! {StringHelper.String(captureError.LocalizedDescription)}"); - - } - _window = new Window(this, layer); _pipeline = new Pipeline(_device, _queue); } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 77594c142..5f1eddabf 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -13,10 +13,13 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Pipeline : IPipeline, IDisposable { - private const int MaxFramesPerCapture = 50; + // 0 Frames = No capture + // Some games like Undertale trigger a stack overflow on capture end + private const int MaxFramesPerCapture = 5; + private const string CaptureLocation = "/Users/isaacmarovitz/Desktop/Captures/Trace-"; private readonly MTLDevice _device; - private readonly MTLCommandQueue _mtlCommandQueue; + private readonly MTLCommandQueue _commandQueue; private readonly HelperShaders _helperShaders; private MTLCommandBuffer _commandBuffer; @@ -29,17 +32,41 @@ namespace Ryujinx.Graphics.Metal private ulong _indexBufferOffset; private MTLClearColor _clearColor; private int _frameCount; - private bool _captureEnded = false; + private bool _captureEnded = true; public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { _device = device; - _mtlCommandQueue = commandQueue; + _commandQueue = commandQueue; _helperShaders = new HelperShaders(_device); _renderEncoderState = new RenderEncoderState(_helperShaders.BlitShader, _device); - _commandBuffer = _mtlCommandQueue.CommandBuffer(); + _commandBuffer = _commandQueue.CommandBuffer(); + + if (MaxFramesPerCapture > 0) + { + StartCapture(); + } + } + + private void StartCapture() + { + var captureDescriptor = new MTLCaptureDescriptor + { + CaptureObject = _commandQueue, + Destination = MTLCaptureDestination.GPUTraceDocument, + OutputURL = NSURL.FileURLWithPath(StringHelper.NSString(CaptureLocation + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".gputrace")) + }; + var captureError = new NSError(IntPtr.Zero); + MTLCaptureManager.SharedCaptureManager().StartCapture(captureDescriptor, ref captureError); + if (captureError != IntPtr.Zero) + { + Console.WriteLine($"Failed to start capture! {StringHelper.String(captureError.LocalizedDescription)}"); + + } + + _captureEnded = false; } public MTLRenderCommandEncoder GetOrCreateRenderEncoder() @@ -160,7 +187,7 @@ namespace Ryujinx.Graphics.Metal } } - _commandBuffer = _mtlCommandQueue.CommandBuffer(); + _commandBuffer = _commandQueue.CommandBuffer(); } public void Barrier() -- 2.47.1 From 7c31a411dfe15482b067bac97c20de0ba2f0fcad Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 17:32:04 -0400 Subject: [PATCH 030/368] SDL2 Headless Metal Backend support --- .../Metal/MetalWindow.cs | 49 +++++++++++++++++++ src/Ryujinx.Headless.SDL2/Program.cs | 16 ++++-- .../Ryujinx.Headless.SDL2.csproj | 1 + 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs diff --git a/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs b/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs new file mode 100644 index 000000000..c37bf2472 --- /dev/null +++ b/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs @@ -0,0 +1,49 @@ +using Ryujinx.Common.Configuration; +using Ryujinx.Input.HLE; +using Ryujinx.SDL2.Common; +using SharpMetal.QuartzCore; +using System.Runtime.Versioning; +using static SDL2.SDL; + +namespace Ryujinx.Headless.SDL2.Metal +{ + [SupportedOSPlatform("macos")] + class MetalWindow : WindowBase + { + private CAMetalLayer _caMetalLayer; + + public CAMetalLayer GetLayer() + { + return _caMetalLayer; + } + + public MetalWindow( + InputManager inputManager, + GraphicsDebugLevel glLogLevel, + AspectRatio aspectRatio, + bool enableMouse, + HideCursorMode hideCursorMode) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode) { } + + public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_METAL; + + protected override void InitializeWindowRenderer() + { + void CreateLayer() + { + _caMetalLayer = new CAMetalLayer(SDL_Metal_GetLayer(SDL_Metal_CreateView(WindowHandle))); + } + + if (SDL2Driver.MainThreadDispatcher != null) + { + SDL2Driver.MainThreadDispatcher(CreateLayer); + } + } + + protected override void InitializeRenderer() { } + + protected override void FinalizeWindowRenderer() { } + + protected override void SwapBuffers() { } + } +} diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 12158176a..da5688a7a 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -21,6 +21,8 @@ using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan.MoltenVK; +using Ryujinx.Graphics.Metal; +using Ryujinx.Headless.SDL2.Metal; using Ryujinx.Headless.SDL2.OpenGL; using Ryujinx.Headless.SDL2.Vulkan; using Ryujinx.HLE; @@ -510,9 +512,12 @@ namespace Ryujinx.Headless.SDL2 private static WindowBase CreateWindow(Options options) { - return options.GraphicsBackend == GraphicsBackend.Vulkan - ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet) - : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet); + return options.GraphicsBackend switch + { + GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet), + GraphicsBackend.Metal => new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet), + _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet) + }; } private static IRenderer CreateRenderer(Options options, WindowBase window) @@ -544,6 +549,11 @@ namespace Ryujinx.Headless.SDL2 preferredGpuId); } + if (options.GraphicsBackend == GraphicsBackend.Metal && window is MetalWindow metalWindow && OperatingSystem.IsMacOS()) + { + return new MetalRenderer(metalWindow.GetLayer); + } + return new OpenGLRenderer(); } diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index fe535e6d5..d39f2b481 100644 --- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -29,6 +29,7 @@ + -- 2.47.1 From 5a802a550b2bab4ee21253804938122f492d12a1 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 19:01:34 -0400 Subject: [PATCH 031/368] Fix Metal Validation Error --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 11 +++++++---- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index a202db426..6d1a2f5e2 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -204,11 +204,14 @@ namespace Ryujinx.Graphics.Metal MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); var span = new Span(mtlBuffer.Contents.ToPointer(), (int)mtlBuffer.Length); data.CopyTo(span[offset..]); - mtlBuffer.DidModifyRange(new NSRange + if (mtlBuffer.StorageMode == MTLStorageMode.Managed) { - location = (ulong)offset, - length = (ulong)data.Length - }); + mtlBuffer.DidModifyRange(new NSRange + { + location = (ulong)offset, + length = (ulong)data.Length + }); + } } public void UpdateCounters() diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 5f1eddabf..8166b8a29 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Metal { // 0 Frames = No capture // Some games like Undertale trigger a stack overflow on capture end - private const int MaxFramesPerCapture = 5; + private const int MaxFramesPerCapture = 0; private const string CaptureLocation = "/Users/isaacmarovitz/Desktop/Captures/Trace-"; private readonly MTLDevice _device; -- 2.47.1 From 4b5c3d7fc6172240e398780b8455206af1f1960c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 23:21:22 -0400 Subject: [PATCH 032/368] More Shader Gen Stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly copied from GLSL since in terms of syntax within blocks they’re pretty similar. Likely the result will need tweaking… Isn’t that conveniant? “Do the simd_shuffle” atomics Remaining instructions Remove removed special instructions Getting somewhere… --- .../CodeGen/Msl/CodeGenContext.cs | 7 + .../CodeGen/Msl/Declarations.cs | 42 ++++ .../CodeGen/Msl/DefaultNames.cs | 15 ++ .../HelperFunctions/HelperFunctionNames.cs | 7 + .../Msl/HelperFunctions/VoteAllEqual.metal | 4 + .../CodeGen/Msl/Instructions/InstGen.cs | 149 ++++++++++++++ .../CodeGen/Msl/Instructions/InstGenHelper.cs | 183 +++++++++++++----- .../CodeGen/Msl/Instructions/InstInfo.cs | 5 +- .../CodeGen/Msl/MslGenerator.cs | 175 +++++++++++++++++ .../CodeGen/Msl/NumberFormatter.cs | 102 ++++++++++ .../CodeGen/Msl/OperandManager.cs | 173 +++++++++++++++++ .../CodeGen/Msl/TypeConversion.cs | 95 +++++++++ .../Ryujinx.Graphics.Shader.csproj | 5 +- 13 files changed, 911 insertions(+), 51 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/VoteAllEqual.metal create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index f41e39625..435da9efb 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -8,9 +8,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { public const string Tab = " "; + public StructuredFunction CurrentFunction { get; set; } + public StructuredProgramInfo Info { get; } + public ShaderConfig Config { get; } + public OperandManager OperandManager { get; } + private readonly StringBuilder _sb; private int _level; @@ -22,6 +27,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl Info = info; Config = config; + OperandManager = new OperandManager(); + _sb = new StringBuilder(); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 11d46278e..ee108893e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -1,4 +1,6 @@ using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; +using System; namespace Ryujinx.Graphics.Shader.CodeGen.Msl { @@ -10,6 +12,46 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine("#include "); context.AppendLine(); context.AppendLine("using namespace metal;"); + + if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) + { + + } + } + + public static void DeclareLocals(CodeGenContext context, StructuredFunction function) + { + foreach (AstOperand decl in function.Locals) + { + string name = context.OperandManager.DeclareLocal(decl); + + context.AppendLine(GetVarTypeName(context, decl.VarType) + " " + name + ";"); + } + } + + public static string GetVarTypeName(CodeGenContext context, AggregateType type) + { + return type switch + { + AggregateType.Void => "void", + AggregateType.Bool => "bool", + AggregateType.FP32 => "float", + AggregateType.S32 => "int", + AggregateType.U32 => "uint", + AggregateType.Vector2 | AggregateType.Bool => "bool2", + AggregateType.Vector2 | AggregateType.FP32 => "float2", + AggregateType.Vector2 | AggregateType.S32 => "int2", + AggregateType.Vector2 | AggregateType.U32 => "uint2", + AggregateType.Vector3 | AggregateType.Bool => "bool3", + AggregateType.Vector3 | AggregateType.FP32 => "float3", + AggregateType.Vector3 | AggregateType.S32 => "int3", + AggregateType.Vector3 | AggregateType.U32 => "uint3", + AggregateType.Vector4 | AggregateType.Bool => "bool4", + AggregateType.Vector4 | AggregateType.FP32 => "float4", + AggregateType.Vector4 | AggregateType.S32 => "int4", + AggregateType.Vector4 | AggregateType.U32 => "uint4", + _ => throw new ArgumentException($"Invalid variable type \"{type}\"."), + }; } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs new file mode 100644 index 000000000..0ec14bfef --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Shader.CodeGen.Msl +{ + static class DefaultNames + { + public const string LocalNamePrefix = "temp"; + + public const string PerPatchAttributePrefix = "patchAttr"; + public const string IAttributePrefix = "inAttr"; + public const string OAttributePrefix = "outAttr"; + + public const string ArgumentNamePrefix = "a"; + + public const string UndefinedName = "undef"; + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs new file mode 100644 index 000000000..1e10f0721 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Shader.CodeGen.Msl +{ + static class HelperFunctionNames + { + public static string SwizzleAdd = "helperSwizzleAdd"; + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/VoteAllEqual.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/VoteAllEqual.metal new file mode 100644 index 000000000..efbcee24d --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/VoteAllEqual.metal @@ -0,0 +1,4 @@ +inline bool voteAllEqual(bool value) +{ + return simd_all(value) || !simd_any(value); +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs new file mode 100644 index 000000000..3f5c5ebda --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -0,0 +1,149 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; +using System; + +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + static class InstGen + { + public static string GetExpression(CodeGenContext context, IAstNode node) + { + if (node is AstOperation operation) + { + return GetExpression(context, operation); + } + else if (node is AstOperand operand) + { + return context.OperandManager.GetExpression(context, operand); + } + + throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); + } + + private static string GetExpression(CodeGenContext context, AstOperation operation) + { + Instruction inst = operation.Inst; + + InstInfo info = GetInstructionInfo(inst); + + if ((info.Type & InstType.Call) != 0) + { + bool atomic = (info.Type & InstType.Atomic) != 0; + + int arity = (int)(info.Type & InstType.ArityMask); + + string args = string.Empty; + + if (atomic) + { + // Hell + } + else + { + for (int argIndex = 0; argIndex < arity; argIndex++) + { + if (argIndex != 0) + { + args += ", "; + } + + AggregateType dstType = GetSrcVarType(inst, argIndex); + + args += GetSourceExpr(context, operation.GetSource(argIndex), dstType); + } + } + + return info.OpName + '(' + args + ')'; + } + else if ((info.Type & InstType.Op) != 0) + { + string op = info.OpName; + + if (inst == Instruction.Return && operation.SourcesCount != 0) + { + return $"{op} {GetSourceExpr(context, operation.GetSource(0), context.CurrentFunction.ReturnType)}"; + } + + int arity = (int)(info.Type & InstType.ArityMask); + + string[] expr = new string[arity]; + + for (int index = 0; index < arity; index++) + { + IAstNode src = operation.GetSource(index); + + string srcExpr = GetSourceExpr(context, src, GetSrcVarType(inst, index)); + + bool isLhs = arity == 2 && index == 0; + + expr[index] = Enclose(srcExpr, src, inst, info, isLhs); + } + + switch (arity) + { + case 0: + return op; + + case 1: + return op + expr[0]; + + case 2: + return $"{expr[0]} {op} {expr[1]}"; + + case 3: + return $"{expr[0]} {op[0]} {expr[1]} {op[1]} {expr[2]}"; + } + } + else if ((info.Type & InstType.Special) != 0) + { + switch (inst & Instruction.Mask) + { + case Instruction.Barrier: + return "|| BARRIER ||"; + case Instruction.Call: + return "|| CALL ||"; + case Instruction.FSIBegin: + return "|| FSI BEGIN ||"; + case Instruction.FSIEnd: + return "|| FSI END ||"; + case Instruction.FindLSB: + return "|| FIND LSB ||"; + case Instruction.FindMSBS32: + return "|| FIND MSB S32 ||"; + case Instruction.FindMSBU32: + return "|| FIND MSB U32 ||"; + case Instruction.GroupMemoryBarrier: + return "|| FIND GROUP MEMORY BARRIER ||"; + case Instruction.ImageLoad: + return "|| IMAGE LOAD ||"; + case Instruction.ImageStore: + return "|| IMAGE STORE ||"; + case Instruction.ImageAtomic: + return "|| IMAGE ATOMIC ||"; + case Instruction.Load: + return "|| LOAD ||"; + case Instruction.Lod: + return "|| LOD ||"; + case Instruction.MemoryBarrier: + return "|| MEMORY BARRIER ||"; + case Instruction.Store: + return "|| STORE ||"; + case Instruction.TextureSample: + return "|| TEXTURE SAMPLE ||"; + case Instruction.TextureSize: + return "|| TEXTURE SIZE ||"; + case Instruction.VectorExtract: + return "|| VECTOR EXTRACT ||"; + case Instruction.VoteAllEqual: + return "|| VOTE ALL EQUAL ||"; + } + } + + throw new InvalidOperationException($"Unexpected instruction type \"{info.Type}\"."); + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index c8757ad35..a74766000 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -1,4 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { @@ -11,42 +13,42 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions _infoTable = new InstInfo[(int)Instruction.Count]; #pragma warning disable IDE0055 // Disable formatting - Add(Instruction.AtomicAdd, InstType.AtomicBinary, "add"); - Add(Instruction.AtomicAnd, InstType.AtomicBinary, "and"); - Add(Instruction.AtomicCompareAndSwap, 0); - Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "max"); - Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "min"); - Add(Instruction.AtomicOr, InstType.AtomicBinary, "or"); - Add(Instruction.AtomicSwap, 0); - Add(Instruction.AtomicXor, InstType.AtomicBinary, "xor"); - Add(Instruction.Absolute, InstType.AtomicBinary, "abs"); - Add(Instruction.Add, InstType.OpBinaryCom, "+"); - Add(Instruction.Ballot, InstType.Special); + Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomic_add_explicit"); + Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomic_and_explicit"); + Add(Instruction.AtomicCompareAndSwap, InstType.AtomicBinary, "atomic_compare_exchange_weak_explicit"); + Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "atomic_max_explicit"); + Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "atomic_min_explicit"); + Add(Instruction.AtomicOr, InstType.AtomicBinary, "atomic_or_explicit"); + Add(Instruction.AtomicSwap, InstType.AtomicBinary, "atomic_exchange_explicit"); + Add(Instruction.AtomicXor, InstType.AtomicBinary, "atomic_xor_explicit"); + Add(Instruction.Absolute, InstType.AtomicBinary, "atomic_abs_explicit"); + Add(Instruction.Add, InstType.OpBinaryCom, "+", 2); + Add(Instruction.Ballot, InstType.CallUnary, "simd_ballot"); Add(Instruction.Barrier, InstType.Special); Add(Instruction.BitCount, InstType.CallUnary, "popcount"); Add(Instruction.BitfieldExtractS32, InstType.CallTernary, "extract_bits"); Add(Instruction.BitfieldExtractU32, InstType.CallTernary, "extract_bits"); Add(Instruction.BitfieldInsert, InstType.CallQuaternary, "insert_bits"); Add(Instruction.BitfieldReverse, InstType.CallUnary, "reverse_bits"); - Add(Instruction.BitwiseAnd, InstType.OpBinaryCom, "&"); - Add(Instruction.BitwiseExclusiveOr, InstType.OpBinaryCom, "^"); - Add(Instruction.BitwiseNot, InstType.OpUnary, "~"); - Add(Instruction.BitwiseOr, InstType.OpBinaryCom, "|"); + Add(Instruction.BitwiseAnd, InstType.OpBinaryCom, "&", 6); + Add(Instruction.BitwiseExclusiveOr, InstType.OpBinaryCom, "^", 7); + Add(Instruction.BitwiseNot, InstType.OpUnary, "~", 0); + Add(Instruction.BitwiseOr, InstType.OpBinaryCom, "|", 8); Add(Instruction.Call, InstType.Special); Add(Instruction.Ceiling, InstType.CallUnary, "ceil"); Add(Instruction.Clamp, InstType.CallTernary, "clamp"); Add(Instruction.ClampU32, InstType.CallTernary, "clamp"); - Add(Instruction.CompareEqual, InstType.OpBinaryCom, "=="); - Add(Instruction.CompareGreater, InstType.OpBinary, ">"); - Add(Instruction.CompareGreaterOrEqual, InstType.OpBinary, ">="); - Add(Instruction.CompareGreaterOrEqualU32, InstType.OpBinary, ">="); - Add(Instruction.CompareGreaterU32, InstType.OpBinary, ">"); - Add(Instruction.CompareLess, InstType.OpBinary, "<"); - Add(Instruction.CompareLessOrEqual, InstType.OpBinary, "<="); - Add(Instruction.CompareLessOrEqualU32, InstType.OpBinary, "<="); - Add(Instruction.CompareLessU32, InstType.OpBinary, "<"); - Add(Instruction.CompareNotEqual, InstType.OpBinaryCom, "!="); - Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:"); + Add(Instruction.CompareEqual, InstType.OpBinaryCom, "==", 5); + Add(Instruction.CompareGreater, InstType.OpBinary, ">", 4); + Add(Instruction.CompareGreaterOrEqual, InstType.OpBinary, ">=", 4); + Add(Instruction.CompareGreaterOrEqualU32, InstType.OpBinary, ">=", 4); + Add(Instruction.CompareGreaterU32, InstType.OpBinary, ">", 4); + Add(Instruction.CompareLess, InstType.OpBinary, "<", 4); + Add(Instruction.CompareLessOrEqual, InstType.OpBinary, "<=", 4); + Add(Instruction.CompareLessOrEqualU32, InstType.OpBinary, "<=", 4); + Add(Instruction.CompareLessU32, InstType.OpBinary, "<", 4); + Add(Instruction.CompareNotEqual, InstType.OpBinaryCom, "!=", 5); + Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:", 12); Add(Instruction.ConvertFP32ToFP64, 0); // MSL does not have a 64-bit FP Add(Instruction.ConvertFP64ToFP32, 0); // MSL does not have a 64-bit FP Add(Instruction.ConvertFP32ToS32, InstType.Cast, "int"); @@ -61,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.Ddx, InstType.CallUnary, "dfdx"); Add(Instruction.Ddy, InstType.CallUnary, "dfdy"); Add(Instruction.Discard, InstType.CallNullary, "discard_fragment"); - Add(Instruction.Divide, InstType.OpBinary, "/"); + Add(Instruction.Divide, InstType.OpBinary, "/", 1); Add(Instruction.EmitVertex, 0); // MSL does not have geometry shaders Add(Instruction.EndPrimitive, 0); // MSL does not have geometry shaders Add(Instruction.ExponentB2, InstType.CallUnary, "exp2"); @@ -81,10 +83,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.Load, InstType.Special); Add(Instruction.Lod, InstType.Special); Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); - Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&"); - Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^"); - Add(Instruction.LogicalNot, InstType.OpUnary, "!"); - Add(Instruction.LogicalOr, InstType.OpBinaryCom, "||"); + Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); + Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^", 10); + Add(Instruction.LogicalNot, InstType.OpUnary, "!", 0); + Add(Instruction.LogicalOr, InstType.OpBinaryCom, "||", 11); Add(Instruction.LoopBreak, InstType.OpNullary, "break"); Add(Instruction.LoopContinue, InstType.OpNullary, "continue"); Add(Instruction.PackDouble2x32, 0); // MSL does not have a 64-bit FP @@ -95,27 +97,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.Minimum, InstType.CallBinary, "min"); Add(Instruction.MinimumU32, InstType.CallBinary, "min"); Add(Instruction.Modulo, InstType.CallBinary, "%"); - Add(Instruction.Multiply, InstType.OpBinaryCom, "*"); - Add(Instruction.MultiplyHighS32, InstType.Special); - Add(Instruction.MultiplyHighU32, InstType.Special); - Add(Instruction.Negate, InstType.Special); + Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1); + Add(Instruction.MultiplyHighS32, InstType.CallBinary, "mulhi"); + Add(Instruction.MultiplyHighU32, InstType.CallBinary, "mulhi"); + Add(Instruction.Negate, InstType.OpUnary, "-"); Add(Instruction.ReciprocalSquareRoot, InstType.CallUnary, "rsqrt"); Add(Instruction.Return, InstType.OpNullary, "return"); Add(Instruction.Round, InstType.CallUnary, "round"); - Add(Instruction.ShiftLeft, InstType.OpBinary, "<<"); - Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>"); - Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>"); - // TODO: Shuffle funcs - Add(Instruction.Shuffle, 0); - Add(Instruction.ShuffleDown, 0); - Add(Instruction.ShuffleUp, 0); - Add(Instruction.ShuffleXor, 0); + Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3); + Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3); + Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3); + Add(Instruction.Shuffle, InstType.CallQuaternary, "simd_shuffle"); + Add(Instruction.ShuffleDown, InstType.CallQuaternary, "simd_shuffle_down"); + Add(Instruction.ShuffleUp, InstType.CallQuaternary, "simd_shuffle_up"); + Add(Instruction.ShuffleXor, InstType.CallQuaternary, "simd_shuffle_xor"); Add(Instruction.Sine, InstType.CallUnary, "sin"); Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); Add(Instruction.Store, InstType.Special); - Add(Instruction.Subtract, InstType.OpBinary, "-"); - // TODO: Swizzle add - Add(Instruction.SwizzleAdd, InstType.Special); + Add(Instruction.Subtract, InstType.OpBinary, "-", 2); + Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); Add(Instruction.TextureSample, InstType.Special); Add(Instruction.TextureSize, InstType.Special); Add(Instruction.Truncate, InstType.CallUnary, "trunc"); @@ -123,15 +123,100 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.UnpackHalf2x16, InstType.CallUnary, "unpack_unorm2x16_to_half"); Add(Instruction.VectorExtract, InstType.Special); Add(Instruction.VoteAll, InstType.CallUnary, "simd_all"); - // TODO: https://github.com/KhronosGroup/SPIRV-Cross/blob/bccaa94db814af33d8ef05c153e7c34d8bd4d685/reference/shaders-msl/comp/shader_group_vote.msl21.comp#L9 Add(Instruction.VoteAllEqual, InstType.Special); Add(Instruction.VoteAny, InstType.CallUnary, "simd_any"); #pragma warning restore IDE0055 } - private static void Add(Instruction inst, InstType flags, string opName = null) + private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0) { - _infoTable[(int)inst] = new InstInfo(flags, opName); + _infoTable[(int)inst] = new InstInfo(flags, opName, precedence); + } + + public static InstInfo GetInstructionInfo(Instruction inst) + { + return _infoTable[(int)(inst & Instruction.Mask)]; + } + + public static string GetSourceExpr(CodeGenContext context, IAstNode node, AggregateType dstType) + { + // TODO: Implement this + // return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType); + return ""; + } + + public static string Enclose(string expr, IAstNode node, Instruction pInst, bool isLhs) + { + InstInfo pInfo = GetInstructionInfo(pInst); + + return Enclose(expr, node, pInst, pInfo, isLhs); + } + + public static string Enclose(string expr, IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs = false) + { + if (NeedsParenthesis(node, pInst, pInfo, isLhs)) + { + expr = "(" + expr + ")"; + } + + return expr; + } + + public static bool NeedsParenthesis(IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs) + { + // If the node isn't a operation, then it can only be a operand, + // and those never needs to be surrounded in parenthesis. + if (node is not AstOperation operation) + { + // This is sort of a special case, if this is a negative constant, + // and it is consumed by a unary operation, we need to put on the parenthesis, + // as in MSL, while a sequence like ~-1 is valid, --2 is not. + if (IsNegativeConst(node) && pInfo.Type == InstType.OpUnary) + { + return true; + } + + return false; + } + + if ((pInfo.Type & (InstType.Call | InstType.Special)) != 0) + { + return false; + } + + InstInfo info = _infoTable[(int)(operation.Inst & Instruction.Mask)]; + + if ((info.Type & (InstType.Call | InstType.Special)) != 0) + { + return false; + } + + if (info.Precedence < pInfo.Precedence) + { + return false; + } + + if (info.Precedence == pInfo.Precedence && isLhs) + { + return false; + } + + if (pInst == operation.Inst && info.Type == InstType.OpBinaryCom) + { + return false; + } + + return true; + } + + private static bool IsNegativeConst(IAstNode node) + { + if (node is not AstOperand operand) + { + return false; + } + + return operand.Type == OperandType.Constant && operand.Value < 0; } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs index 62cbda199..5e5d04d6b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs @@ -6,10 +6,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions public string OpName { get; } - public InstInfo(InstType type, string opName) + public int Precedence { get; } + + public InstInfo(InstType type, string opName, int precedence) { Type = type; OpName = opName; + Precedence = precedence; } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 71196b124..b11a7452d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -1,6 +1,10 @@ using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; +using System; + +using static Ryujinx.Graphics.Shader.CodeGen.Msl.TypeConversion; namespace Ryujinx.Graphics.Shader.CodeGen.Msl { @@ -18,7 +22,178 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl Declarations.Declare(context, info); + if (info.Functions.Count != 0) + { + for (int i = 1; i < info.Functions.Count; i++) + { + context.AppendLine($"{GetFunctionSignature(context, info.Functions[i], config.Stage)};"); + } + + context.AppendLine(); + + for (int i = 1; i < info.Functions.Count; i++) + { + PrintFunction(context, info.Functions[i], config.Stage); + + context.AppendLine(); + } + } + + PrintFunction(context, info.Functions[0], config.Stage, true); + return context.GetCode(); } + + private static void PrintFunction(CodeGenContext context, StructuredFunction function, ShaderStage stage, bool isMainFunc = false) + { + context.CurrentFunction = function; + + context.AppendLine(GetFunctionSignature(context, function, stage, isMainFunc)); + context.EnterScope(); + + Declarations.DeclareLocals(context, function); + + PrintBlock(context, function.MainBlock, isMainFunc); + + context.LeaveScope(); + } + + private static string GetFunctionSignature(CodeGenContext context, StructuredFunction function, ShaderStage stage, bool isMainFunc = false) + { + string[] args = new string[function.InArguments.Length + function.OutArguments.Length]; + + for (int i = 0; i < function.InArguments.Length; i++) + { + args[i] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; + } + + for (int i = 0; i < function.OutArguments.Length; i++) + { + int j = i + function.InArguments.Length; + + // Likely need to be made into pointers + args[j] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; + } + + string funcKeyword = "inline"; + string funcName = null; + if (isMainFunc) + { + if (stage == ShaderStage.Vertex) + { + funcKeyword = "vertex"; + funcName = "vertexMain"; + } + else if (stage == ShaderStage.Fragment) + { + funcKeyword = "fragment"; + funcName = "fragmentMain"; + } + } + + return $"{funcKeyword} {Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})"; + } + + private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction) + { + AstBlockVisitor visitor = new(block); + + visitor.BlockEntered += (sender, e) => + { + switch (e.Block.Type) + { + case AstBlockType.DoWhile: + context.AppendLine("do"); + break; + + case AstBlockType.Else: + context.AppendLine("else"); + break; + + case AstBlockType.ElseIf: + context.AppendLine($"else if ({GetCondExpr(context, e.Block.Condition)})"); + break; + + case AstBlockType.If: + context.AppendLine($"if ({GetCondExpr(context, e.Block.Condition)})"); + break; + + default: + throw new InvalidOperationException($"Found unexpected block type \"{e.Block.Type}\"."); + } + + context.EnterScope(); + }; + + visitor.BlockLeft += (sender, e) => + { + context.LeaveScope(); + + if (e.Block.Type == AstBlockType.DoWhile) + { + context.AppendLine($"while ({GetCondExpr(context, e.Block.Condition)});"); + } + }; + + bool supportsBarrierDivergence = context.Config.GpuAccessor.QueryHostSupportsShaderBarrierDivergence(); + bool mayHaveReturned = false; + + foreach (IAstNode node in visitor.Visit()) + { + if (node is AstOperation operation) + { + if (!supportsBarrierDivergence) + { + if (operation.Inst == IntermediateRepresentation.Instruction.Barrier) + { + // Barrier on divergent control flow paths may cause the GPU to hang, + // so skip emitting the barrier for those cases. + if (visitor.Block.Type != AstBlockType.Main || mayHaveReturned || !isMainFunction) + { + context.Config.GpuAccessor.Log($"Shader has barrier on potentially divergent block, the barrier will be removed."); + + continue; + } + } + else if (operation.Inst == IntermediateRepresentation.Instruction.Return) + { + mayHaveReturned = true; + } + } + + string expr = InstGen.GetExpression(context, operation); + + if (expr != null) + { + context.AppendLine(expr + ";"); + } + } + else if (node is AstAssignment assignment) + { + AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination); + AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source); + + string dest = InstGen.GetExpression(context, assignment.Destination); + string src = ReinterpretCast(context, assignment.Source, srcType, dstType); + + context.AppendLine(dest + " = " + src + ";"); + } + else if (node is AstComment comment) + { + context.AppendLine("// " + comment.Comment); + } + else + { + throw new InvalidOperationException($"Found unexpected node type \"{node?.GetType().Name ?? "null"}\"."); + } + } + } + + private static string GetCondExpr(CodeGenContext context, IAstNode cond) + { + AggregateType srcType = OperandManager.GetNodeDestType(context, cond); + + return ReinterpretCast(context, cond, srcType, AggregateType.Bool); + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs new file mode 100644 index 000000000..f086e7436 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs @@ -0,0 +1,102 @@ +using Ryujinx.Graphics.Shader.Translation; +using System; +using System.Globalization; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl +{ + static class NumberFormatter + { + private const int MaxDecimal = 256; + + public static bool TryFormat(int value, AggregateType dstType, out string formatted) + { + if (dstType == AggregateType.FP32) + { + return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted); + } + else if (dstType == AggregateType.S32) + { + formatted = FormatInt(value); + } + else if (dstType == AggregateType.U32) + { + formatted = FormatUint((uint)value); + } + else if (dstType == AggregateType.Bool) + { + formatted = value != 0 ? "true" : "false"; + } + else + { + throw new ArgumentException($"Invalid variable type \"{dstType}\"."); + } + + return true; + } + + public static string FormatFloat(float value) + { + if (!TryFormatFloat(value, out string formatted)) + { + throw new ArgumentException("Failed to convert float value to string."); + } + + return formatted; + } + + public static bool TryFormatFloat(float value, out string formatted) + { + if (float.IsNaN(value) || float.IsInfinity(value)) + { + formatted = null; + + return false; + } + + formatted = value.ToString("F9", CultureInfo.InvariantCulture); + + if (!formatted.Contains('.')) + { + formatted += ".0f"; + } + + return true; + } + + public static string FormatInt(int value, AggregateType dstType) + { + if (dstType == AggregateType.S32) + { + return FormatInt(value); + } + else if (dstType == AggregateType.U32) + { + return FormatUint((uint)value); + } + else + { + throw new ArgumentException($"Invalid variable type \"{dstType}\"."); + } + } + + public static string FormatInt(int value) + { + if (value <= MaxDecimal && value >= -MaxDecimal) + { + return value.ToString(CultureInfo.InvariantCulture); + } + + return "0x" + value.ToString("X", CultureInfo.InvariantCulture); + } + + public static string FormatUint(uint value) + { + if (value <= MaxDecimal && value >= 0) + { + return value.ToString(CultureInfo.InvariantCulture) + "u"; + } + + return "0x" + value.ToString("X", CultureInfo.InvariantCulture) + "u"; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs new file mode 100644 index 000000000..4aa177afa --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs @@ -0,0 +1,173 @@ +using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl +{ + class OperandManager + { + private readonly Dictionary _locals; + + public OperandManager() + { + _locals = new Dictionary(); + } + + public string DeclareLocal(AstOperand operand) + { + string name = $"{DefaultNames.LocalNamePrefix}_{_locals.Count}"; + + _locals.Add(operand, name); + + return name; + } + + public string GetExpression(CodeGenContext context, AstOperand operand) + { + return operand.Type switch + { + OperandType.Argument => GetArgumentName(operand.Value), + OperandType.Constant => NumberFormatter.FormatInt(operand.Value), + OperandType.LocalVariable => _locals[operand], + OperandType.Undefined => DefaultNames.UndefinedName, + _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."), + }; + } + + public static string GetArgumentName(int argIndex) + { + return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; + } + + public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node) + { + // TODO: Get rid of that function entirely and return the type from the operation generation + // functions directly, like SPIR-V does. + + if (node is AstOperation operation) + { + if (operation.Inst == Instruction.Load || operation.Inst.IsAtomic()) + { + switch (operation.StorageKind) + { + case StorageKind.ConstantBuffer: + case StorageKind.StorageBuffer: + if (operation.GetSource(0) is not AstOperand bindingIndex || bindingIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } + + if (operation.GetSource(1) is not AstOperand fieldIndex || fieldIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } + + BufferDefinition buffer = operation.StorageKind == StorageKind.ConstantBuffer + ? context.Config.Properties.ConstantBuffers[bindingIndex.Value] + : context.Config.Properties.StorageBuffers[bindingIndex.Value]; + StructureField field = buffer.Type.Fields[fieldIndex.Value]; + + return field.Type & AggregateType.ElementTypeMask; + + case StorageKind.LocalMemory: + case StorageKind.SharedMemory: + if (operation.GetSource(0) is not AstOperand { Type: OperandType.Constant } bindingId) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } + + MemoryDefinition memory = operation.StorageKind == StorageKind.LocalMemory + ? context.Config.Properties.LocalMemories[bindingId.Value] + : context.Config.Properties.SharedMemories[bindingId.Value]; + + return memory.Type & AggregateType.ElementTypeMask; + + case StorageKind.Input: + case StorageKind.InputPerPatch: + case StorageKind.Output: + case StorageKind.OutputPerPatch: + if (operation.GetSource(0) is not AstOperand varId || varId.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } + + IoVariable ioVariable = (IoVariable)varId.Value; + bool isOutput = operation.StorageKind == StorageKind.Output || operation.StorageKind == StorageKind.OutputPerPatch; + bool isPerPatch = operation.StorageKind == StorageKind.InputPerPatch || operation.StorageKind == StorageKind.OutputPerPatch; + int location = 0; + int component = 0; + + if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + { + if (operation.GetSource(1) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } + + location = vecIndex.Value; + + if (operation.SourcesCount > 2 && + operation.GetSource(2) is AstOperand elemIndex && + elemIndex.Type == OperandType.Constant && + context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + { + component = elemIndex.Value; + } + } + + (_, AggregateType varType) = IoMap.GetMSLBuiltIn(ioVariable); + + return varType & AggregateType.ElementTypeMask; + } + } + else if (operation.Inst == Instruction.Call) + { + AstOperand funcId = (AstOperand)operation.GetSource(0); + + Debug.Assert(funcId.Type == OperandType.Constant); + + return context.GetFunction(funcId.Value).ReturnType; + } + else if (operation.Inst == Instruction.VectorExtract) + { + return GetNodeDestType(context, operation.GetSource(0)) & ~AggregateType.ElementCountMask; + } + else if (operation is AstTextureOperation texOp) + { + if (texOp.Inst == Instruction.ImageLoad || + texOp.Inst == Instruction.ImageStore || + texOp.Inst == Instruction.ImageAtomic) + { + return texOp.GetVectorType(texOp.Format.GetComponentType()); + } + else if (texOp.Inst == Instruction.TextureSample) + { + return texOp.GetVectorType(GetDestVarType(operation.Inst)); + } + } + + return GetDestVarType(operation.Inst); + } + else if (node is AstOperand operand) + { + if (operand.Type == OperandType.Argument) + { + int argIndex = operand.Value; + + return context.CurrentFunction.GetArgumentType(argIndex); + } + + return OperandInfo.GetVarType(operand); + } + else + { + throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); + } + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs new file mode 100644 index 000000000..edbdd77dd --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs @@ -0,0 +1,95 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl +{ + static class TypeConversion + { +public static string ReinterpretCast( + CodeGenContext context, + IAstNode node, + AggregateType srcType, + AggregateType dstType) + { + if (node is AstOperand operand && operand.Type == OperandType.Constant) + { + if (NumberFormatter.TryFormat(operand.Value, dstType, out string formatted)) + { + return formatted; + } + } + + string expr = InstGen.GetExpression(context, node); + + return ReinterpretCast(expr, node, srcType, dstType); + } + + private static string ReinterpretCast(string expr, IAstNode node, AggregateType srcType, AggregateType dstType) + { + if (srcType == dstType) + { + return expr; + } + + if (srcType == AggregateType.FP32) + { + switch (dstType) + { + case AggregateType.Bool: + return $"(floatBitsToInt({expr}) != 0)"; + case AggregateType.S32: + return $"floatBitsToInt({expr})"; + case AggregateType.U32: + return $"floatBitsToUint({expr})"; + } + } + else if (dstType == AggregateType.FP32) + { + switch (srcType) + { + case AggregateType.Bool: + return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, AggregateType.S32)})"; + case AggregateType.S32: + return $"intBitsToFloat({expr})"; + case AggregateType.U32: + return $"uintBitsToFloat({expr})"; + } + } + else if (srcType == AggregateType.Bool) + { + return ReinterpretBoolToInt(expr, node, dstType); + } + else if (dstType == AggregateType.Bool) + { + expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true); + + return $"({expr} != 0)"; + } + else if (dstType == AggregateType.S32) + { + return $"int({expr})"; + } + else if (dstType == AggregateType.U32) + { + return $"uint({expr})"; + } + + Logger.Warning?.Print(LogClass.Gpu, $"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\"."); + // TODO: Make this an error again + return $"INVALID CAST ({expr})"; + } + + private static string ReinterpretBoolToInt(string expr, IAstNode node, AggregateType dstType) + { + string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType); + string falseExpr = NumberFormatter.FormatInt(IrConsts.False, dstType); + + expr = InstGenHelper.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false); + + return $"({expr} ? {trueExpr} : {falseExpr})"; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index cfb188daf..4811b69de 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -14,5 +14,8 @@ - + + + + -- 2.47.1 From dc4d3078efdd7d88f76dd2d4ec6ef71f0ffd9d6f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 4 Aug 2023 23:51:24 -0400 Subject: [PATCH 033/368] Vertex Input Attributes --- .../CodeGen/Msl/CodeGenContext.cs | 2 +- .../CodeGen/Msl/Declarations.cs | 29 +++++++++++++++++++ .../CodeGen/Msl/MslGenerator.cs | 8 ++++- .../CodeGen/Msl/OperandManager.cs | 8 ++--- .../CodeGen/Msl/TypeConversion.cs | 10 +++---- .../Metal/MetalWindow.cs | 5 +--- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index 435da9efb..551e7c281 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public ShaderConfig Config { get; } - public OperandManager OperandManager { get; } + public OperandManager OperandManager { get; } private readonly StringBuilder _sb; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index ee108893e..99f838d66 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -1,6 +1,9 @@ using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; +using System.Collections; +using System.Collections.Generic; +using System.Numerics; namespace Ryujinx.Graphics.Shader.CodeGen.Msl { @@ -12,11 +15,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine("#include "); context.AppendLine(); context.AppendLine("using namespace metal;"); + context.AppendLine(); if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) { } + + DeclareInputAttributes(context, info); } public static void DeclareLocals(CodeGenContext context, StructuredFunction function) @@ -53,5 +59,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl _ => throw new ArgumentException($"Invalid variable type \"{type}\"."), }; } + + private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info) + { + if (context.Config.UsedInputAttributes != 0) + { + context.AppendLine("struct VertexIn"); + context.EnterScope(); + + int usedAttributes = context.Config.UsedInputAttributes | context.Config.PassthroughAttributes; + while (usedAttributes != 0) + { + int index = BitOperations.TrailingZeroCount(usedAttributes); + + string name = $"{DefaultNames.IAttributePrefix}{index}"; + var type = context.Config.GpuAccessor.QueryAttributeType(index).ToVec4Type(TargetLanguage.Msl); + context.AppendLine($"{type} {name} [[attribute({index})]];"); + + usedAttributes &= ~(1 << index); + } + + context.LeaveScope(";"); + } + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index b11a7452d..31d370255 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; - +using System.Linq; using static Ryujinx.Graphics.Shader.CodeGen.Msl.TypeConversion; namespace Ryujinx.Graphics.Shader.CodeGen.Msl @@ -77,6 +77,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl string funcKeyword = "inline"; string funcName = null; + if (isMainFunc) { if (stage == ShaderStage.Vertex) @@ -89,6 +90,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl funcKeyword = "fragment"; funcName = "fragmentMain"; } + + if (context.Config.UsedInputAttributes != 0) + { + args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); + } } return $"{funcKeyword} {Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs index 4aa177afa..389948169 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { class OperandManager { - private readonly Dictionary _locals; + private readonly Dictionary _locals; public OperandManager() { @@ -99,8 +99,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable ioVariable = (IoVariable)varId.Value; bool isOutput = operation.StorageKind == StorageKind.Output || operation.StorageKind == StorageKind.OutputPerPatch; bool isPerPatch = operation.StorageKind == StorageKind.InputPerPatch || operation.StorageKind == StorageKind.OutputPerPatch; - int location = 0; - int component = 0; if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) { @@ -109,14 +107,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); } - location = vecIndex.Value; + int location = vecIndex.Value; if (operation.SourcesCount > 2 && operation.GetSource(2) is AstOperand elemIndex && elemIndex.Type == OperandType.Constant && context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) { - component = elemIndex.Value; + int component = elemIndex.Value; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs index edbdd77dd..44666c323 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs @@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { static class TypeConversion { -public static string ReinterpretCast( - CodeGenContext context, - IAstNode node, - AggregateType srcType, - AggregateType dstType) + public static string ReinterpretCast( + CodeGenContext context, + IAstNode node, + AggregateType srcType, + AggregateType dstType) { if (node is AstOperand operand && operand.Type == OperandType.Constant) { diff --git a/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs b/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs index c37bf2472..e10d6eb29 100644 --- a/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs +++ b/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs @@ -34,10 +34,7 @@ namespace Ryujinx.Headless.SDL2.Metal _caMetalLayer = new CAMetalLayer(SDL_Metal_GetLayer(SDL_Metal_CreateView(WindowHandle))); } - if (SDL2Driver.MainThreadDispatcher != null) - { - SDL2Driver.MainThreadDispatcher(CreateLayer); - } + SDL2Driver.MainThreadDispatcher?.Invoke(CreateLayer); } protected override void InitializeRenderer() { } -- 2.47.1 From 84c90f88954534d3a3d779ee583b5e522e2e0664 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:42:41 +0100 Subject: [PATCH 034/368] Update src/Ryujinx.Graphics.Metal/Pipeline.cs Co-authored-by: gdkchan --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 8166b8a29..2c1e0d4e3 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Metal public void SetDepthTest(DepthTestDescriptor depthTest) { var depthStencilState = _renderEncoderState.UpdateDepthState( - depthTest.TestEnable ? MTLCompareFunction.Always : depthTest.Func.Convert(), + depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always, depthTest.WriteEnable); if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) -- 2.47.1 From b1785c0b143e4a20c98de19923716845493f1fc2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 12 Aug 2023 15:12:35 +0100 Subject: [PATCH 035/368] Update for new Shader IR format --- src/Ryujinx.Graphics.Metal/Window.cs | 2 + .../CodeGen/Msl/CodeGenContext.cs | 16 +++++- .../CodeGen/Msl/Declarations.cs | 55 +++++++++++-------- .../CodeGen/Msl/MslGenerator.cs | 20 +++---- .../CodeGen/Msl/OperandManager.cs | 12 ++-- 5 files changed, 62 insertions(+), 43 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index f8ddca3fe..a656ce26b 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -54,6 +54,8 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } + public void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled) { } + public void Dispose() { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index 551e7c281..cf1ad906f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -12,7 +12,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public StructuredProgramInfo Info { get; } - public ShaderConfig Config { get; } + public AttributeUsage AttributeUsage { get; } + public ShaderDefinitions Definitions { get; } + public ShaderProperties Properties { get; } + public HostCapabilities HostCapabilities { get; } + public ILogger Logger { get; } + public TargetApi TargetApi { get; } public OperandManager OperandManager { get; } @@ -22,10 +27,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl private string _indentation; - public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) + public CodeGenContext(StructuredProgramInfo info, CodeGenParameters parameters) { Info = info; - Config = config; + AttributeUsage = parameters.AttributeUsage; + Definitions = parameters.Definitions; + Properties = parameters.Properties; + HostCapabilities = parameters.HostCapabilities; + Logger = parameters.Logger; + TargetApi = parameters.TargetApi; OperandManager = new OperandManager(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 99f838d66..523aa56f3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -1,8 +1,9 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; -using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Numerics; namespace Ryujinx.Graphics.Shader.CodeGen.Msl @@ -22,7 +23,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } - DeclareInputAttributes(context, info); + // DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); + } + + static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) + { + return ioDefinition.StorageKind == storageKind && ioDefinition.IoVariable == IoVariable.UserDefined; } public static void DeclareLocals(CodeGenContext context, StructuredFunction function) @@ -60,27 +66,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl }; } - private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info) - { - if (context.Config.UsedInputAttributes != 0) - { - context.AppendLine("struct VertexIn"); - context.EnterScope(); - - int usedAttributes = context.Config.UsedInputAttributes | context.Config.PassthroughAttributes; - while (usedAttributes != 0) - { - int index = BitOperations.TrailingZeroCount(usedAttributes); - - string name = $"{DefaultNames.IAttributePrefix}{index}"; - var type = context.Config.GpuAccessor.QueryAttributeType(index).ToVec4Type(TargetLanguage.Msl); - context.AppendLine($"{type} {name} [[attribute({index})]];"); - - usedAttributes &= ~(1 << index); - } - - context.LeaveScope(";"); - } - } + // TODO: Redo for new Shader IR rep + // private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) + // { + // if (context.AttributeUsage.UsedInputAttributes != 0) + // { + // context.AppendLine("struct VertexIn"); + // context.EnterScope(); + // + // int usedAttributes = context.AttributeUsage.UsedInputAttributes | context.AttributeUsage.PassthroughAttributes; + // while (usedAttributes != 0) + // { + // int index = BitOperations.TrailingZeroCount(usedAttributes); + // + // string name = $"{DefaultNames.IAttributePrefix}{index}"; + // var type = context.AttributeUsage.get .QueryAttributeType(index).ToVec4Type(TargetLanguage.Msl); + // context.AppendLine($"{type} {name} [[attribute({index})]];"); + // + // usedAttributes &= ~(1 << index); + // } + // + // context.LeaveScope(";"); + // } + // } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 31d370255..e0ce97abe 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -10,15 +10,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { static class MslGenerator { - public static string Generate(StructuredProgramInfo info, ShaderConfig config) + public static string Generate(StructuredProgramInfo info, CodeGenParameters parameters) { - if (config.Stage is not (ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute)) + if (parameters.Definitions.Stage is not (ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute)) { - Logger.Warning?.Print(LogClass.Gpu, $"Attempted to generate unsupported shader type {config.Stage}!"); + Logger.Warning?.Print(LogClass.Gpu, $"Attempted to generate unsupported shader type {parameters.Definitions.Stage}!"); return ""; } - CodeGenContext context = new(info, config); + CodeGenContext context = new(info, parameters); Declarations.Declare(context, info); @@ -26,20 +26,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { for (int i = 1; i < info.Functions.Count; i++) { - context.AppendLine($"{GetFunctionSignature(context, info.Functions[i], config.Stage)};"); + context.AppendLine($"{GetFunctionSignature(context, info.Functions[i], parameters.Definitions.Stage)};"); } context.AppendLine(); for (int i = 1; i < info.Functions.Count; i++) { - PrintFunction(context, info.Functions[i], config.Stage); + PrintFunction(context, info.Functions[i], parameters.Definitions.Stage); context.AppendLine(); } } - PrintFunction(context, info.Functions[0], config.Stage, true); + PrintFunction(context, info.Functions[0], parameters.Definitions.Stage, true); return context.GetCode(); } @@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl funcName = "fragmentMain"; } - if (context.Config.UsedInputAttributes != 0) + if (context.AttributeUsage.UsedInputAttributes != 0) { args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); } @@ -141,7 +141,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } }; - bool supportsBarrierDivergence = context.Config.GpuAccessor.QueryHostSupportsShaderBarrierDivergence(); + bool supportsBarrierDivergence = context.HostCapabilities.SupportsShaderBarrierDivergence; bool mayHaveReturned = false; foreach (IAstNode node in visitor.Visit()) @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl // so skip emitting the barrier for those cases. if (visitor.Block.Type != AstBlockType.Main || mayHaveReturned || !isMainFunction) { - context.Config.GpuAccessor.Log($"Shader has barrier on potentially divergent block, the barrier will be removed."); + context.Logger.Log($"Shader has barrier on potentially divergent block, the barrier will be removed."); continue; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs index 389948169..beaf25f68 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs @@ -68,8 +68,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } BufferDefinition buffer = operation.StorageKind == StorageKind.ConstantBuffer - ? context.Config.Properties.ConstantBuffers[bindingIndex.Value] - : context.Config.Properties.StorageBuffers[bindingIndex.Value]; + ? context.Properties.ConstantBuffers[bindingIndex.Value] + : context.Properties.StorageBuffers[bindingIndex.Value]; StructureField field = buffer.Type.Fields[fieldIndex.Value]; return field.Type & AggregateType.ElementTypeMask; @@ -82,8 +82,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } MemoryDefinition memory = operation.StorageKind == StorageKind.LocalMemory - ? context.Config.Properties.LocalMemories[bindingId.Value] - : context.Config.Properties.SharedMemories[bindingId.Value]; + ? context.Properties.LocalMemories[bindingId.Value] + : context.Properties.SharedMemories[bindingId.Value]; return memory.Type & AggregateType.ElementTypeMask; @@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl bool isOutput = operation.StorageKind == StorageKind.Output || operation.StorageKind == StorageKind.OutputPerPatch; bool isPerPatch = operation.StorageKind == StorageKind.InputPerPatch || operation.StorageKind == StorageKind.OutputPerPatch; - if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) { if (operation.GetSource(1) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) { @@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl if (operation.SourcesCount > 2 && operation.GetSource(2) is AstOperand elemIndex && elemIndex.Type == OperandType.Constant && - context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) { int component = elemIndex.Value; } -- 2.47.1 From 94e699eebacaad8c2183a4030dd8aaaab8e0d4f4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 14 Aug 2023 12:12:44 +0100 Subject: [PATCH 036/368] Boot Sonic Mania --- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 21932933a..c57a01b11 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLTextureDescriptor { PixelFormat = FormatTable.GetFormat(Info.Format), - Usage = MTLTextureUsage.ShaderRead | MTLTextureUsage.ShaderWrite | MTLTextureUsage.RenderTarget, + Usage = MTLTextureUsage.ShaderRead, Width = (ulong)Width, Height = (ulong)Height, Depth = (ulong)Depth -- 2.47.1 From a3da70edc22893c5ff04e2439ad563d9d4db52be Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 14 Aug 2023 12:17:22 +0100 Subject: [PATCH 037/368] Boot TOTK --- src/Ryujinx.Graphics.Metal/Texture.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index c57a01b11..d771f9a22 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -119,7 +119,8 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + return this; } public PinnedSpan GetData() -- 2.47.1 From c5522e3694ec2bd157bae6f268c2cb9d482a3284 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 15 Aug 2023 14:17:00 +0100 Subject: [PATCH 038/368] Back to where we were First special instruction Start Load/Store implementation Start TextureSample Sample progress I/O Load/Store Progress Rest of load/store TODO: Currently, the generator still assumes the GLSL style of I/O attributres. On MSL, the vertex function should output a struct which contains a float4 with the required position attribute. TextureSize and VectorExtract Fix UserDefined IO Vars Fix stage input struct names --- .../CodeGen/Msl/Declarations.cs | 66 ++-- .../CodeGen/Msl/Instructions/InstGen.cs | 18 +- .../CodeGen/Msl/Instructions/InstGenCall.cs | 25 ++ .../CodeGen/Msl/Instructions/InstGenHelper.cs | 6 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 318 ++++++++++++++++++ .../CodeGen/Msl/Instructions/InstGenVector.cs | 32 ++ .../CodeGen/Msl/Instructions/IoMap.cs | 49 ++- .../CodeGen/Msl/MslGenerator.cs | 17 +- .../CodeGen/Msl/OperandManager.cs | 17 +- .../Translation/TranslatorContext.cs | 2 + 10 files changed, 507 insertions(+), 43 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 523aa56f3..f06af235a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; +using System.Data.Common; using System.Linq; using System.Numerics; @@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } - // DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); + DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); } static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) @@ -66,28 +67,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl }; } - // TODO: Redo for new Shader IR rep - // private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) - // { - // if (context.AttributeUsage.UsedInputAttributes != 0) - // { - // context.AppendLine("struct VertexIn"); - // context.EnterScope(); - // - // int usedAttributes = context.AttributeUsage.UsedInputAttributes | context.AttributeUsage.PassthroughAttributes; - // while (usedAttributes != 0) - // { - // int index = BitOperations.TrailingZeroCount(usedAttributes); - // - // string name = $"{DefaultNames.IAttributePrefix}{index}"; - // var type = context.AttributeUsage.get .QueryAttributeType(index).ToVec4Type(TargetLanguage.Msl); - // context.AppendLine($"{type} {name} [[attribute({index})]];"); - // - // usedAttributes &= ~(1 << index); - // } - // - // context.LeaveScope(";"); - // } - // } + private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) + { + if (context.Definitions.IaIndexing) + { + // Not handled + } + else + { + if (inputs.Any()) + { + string prefix = ""; + + switch (context.Definitions.Stage) + { + case ShaderStage.Vertex: + prefix = "Vertex"; + break; + case ShaderStage.Fragment: + prefix = "Fragment"; + break; + case ShaderStage.Compute: + prefix = "Compute"; + break; + } + + context.AppendLine($"struct {prefix}In"); + context.EnterScope(); + + foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) + { + string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)); + string name = $"{DefaultNames.IAttributePrefix}{ioDefinition.Location}"; + + context.AppendLine($"{type} {name} [[attribute({ioDefinition.Location})]];"); + } + + context.LeaveScope(";"); + } + } + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 3f5c5ebda..ffb2105ea 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -3,7 +3,10 @@ using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenCall; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenMemory; +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenVector; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions @@ -105,7 +108,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.Barrier: return "|| BARRIER ||"; case Instruction.Call: - return "|| CALL ||"; + return Call(context, operation); case Instruction.FSIBegin: return "|| FSI BEGIN ||"; case Instruction.FSIEnd: @@ -125,25 +128,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.ImageAtomic: return "|| IMAGE ATOMIC ||"; case Instruction.Load: - return "|| LOAD ||"; + return Load(context, operation); case Instruction.Lod: return "|| LOD ||"; case Instruction.MemoryBarrier: return "|| MEMORY BARRIER ||"; case Instruction.Store: - return "|| STORE ||"; + return Store(context, operation); case Instruction.TextureSample: - return "|| TEXTURE SAMPLE ||"; + return TextureSample(context, operation); case Instruction.TextureSize: - return "|| TEXTURE SIZE ||"; + return TextureSize(context, operation); case Instruction.VectorExtract: - return "|| VECTOR EXTRACT ||"; + return VectorExtract(context, operation); case Instruction.VoteAllEqual: return "|| VOTE ALL EQUAL ||"; } } - throw new InvalidOperationException($"Unexpected instruction type \"{info.Type}\"."); + // TODO: Return this to being an error + return $"Unexpected instruction type \"{info.Type}\"."; } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs new file mode 100644 index 000000000..df9d10301 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.Shader.StructuredIr; + +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + static class InstGenCall + { + public static string Call(CodeGenContext context, AstOperation operation) + { + AstOperand funcId = (AstOperand)operation.GetSource(0); + + var functon = context.GetFunction(funcId.Value); + + string[] args = new string[operation.SourcesCount - 1]; + + for (int i = 0; i < args.Length; i++) + { + args[i] = GetSourceExpr(context, operation.GetSource(i + 1), functon.GetArgumentType(i)); + } + + return $"{functon.Name}({string.Join(", ", args)})"; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index a74766000..489787747 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -2,6 +2,8 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; +using static Ryujinx.Graphics.Shader.CodeGen.Msl.TypeConversion; + namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { static class InstGenHelper @@ -140,9 +142,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions public static string GetSourceExpr(CodeGenContext context, IAstNode node, AggregateType dstType) { - // TODO: Implement this - // return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType); - return ""; + return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType); } public static string Enclose(string expr, IAstNode node, Instruction pInst, bool isLhs) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs new file mode 100644 index 000000000..2f0434c96 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -0,0 +1,318 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; +using System; +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + static class InstGenMemory + { + public static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) + { + StorageKind storageKind = operation.StorageKind; + + string varName; + AggregateType varType; + int srcIndex = 0; + bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); + int inputsCount = isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount; + + if (operation.Inst == Instruction.AtomicCompareAndSwap) + { + inputsCount--; + } + + switch (storageKind) + { + case StorageKind.ConstantBuffer: + case StorageKind.StorageBuffer: + if (operation.GetSource(srcIndex++) is not AstOperand bindingIndex || bindingIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + int binding = bindingIndex.Value; + BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer + ? context.Properties.ConstantBuffers[binding] + : context.Properties.StorageBuffers[binding]; + + if (operation.GetSource(srcIndex++) is not AstOperand fieldIndex || fieldIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + StructureField field = buffer.Type.Fields[fieldIndex.Value]; + varName = $"{buffer.Name}.{field.Name}"; + varType = field.Type; + break; + + case StorageKind.LocalMemory: + case StorageKind.SharedMemory: + if (operation.GetSource(srcIndex++) is not AstOperand { Type: OperandType.Constant } bindingId) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + MemoryDefinition memory = storageKind == StorageKind.LocalMemory + ? context.Properties.LocalMemories[bindingId.Value] + : context.Properties.SharedMemories[bindingId.Value]; + + varName = memory.Name; + varType = memory.Type; + break; + + case StorageKind.Input: + case StorageKind.InputPerPatch: + case StorageKind.Output: + case StorageKind.OutputPerPatch: + if (operation.GetSource(srcIndex++) is not AstOperand varId || varId.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + IoVariable ioVariable = (IoVariable)varId.Value; + bool isOutput = storageKind.IsOutput(); + bool isPerPatch = storageKind.IsPerPatch(); + int location = -1; + int component = 0; + + if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) + { + if (operation.GetSource(srcIndex++) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + location = vecIndex.Value; + + if (operation.SourcesCount > srcIndex && + operation.GetSource(srcIndex) is AstOperand elemIndex && + elemIndex.Type == OperandType.Constant && + context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, vecIndex.Value, elemIndex.Value, isOutput)) + { + component = elemIndex.Value; + srcIndex++; + } + } + + (varName, varType) = IoMap.GetMslBuiltIn( + context.Definitions, + ioVariable, + location, + component, + isOutput, + isPerPatch); + break; + + default: + throw new InvalidOperationException($"Invalid storage kind {storageKind}."); + } + + for (; srcIndex < inputsCount; srcIndex++) + { + IAstNode src = operation.GetSource(srcIndex); + + if ((varType & AggregateType.ElementCountMask) != 0 && + srcIndex == inputsCount - 1 && + src is AstOperand elementIndex && + elementIndex.Type == OperandType.Constant) + { + varName += "." + "xyzw"[elementIndex.Value & 3]; + } + else + { + varName += $"[{GetSourceExpr(context, src, AggregateType.S32)}]"; + } + } + + if (isStore) + { + varType &= AggregateType.ElementTypeMask; + varName = $"{varName} = {GetSourceExpr(context, operation.GetSource(srcIndex), varType)}"; + } + + return varName; + } + + public static string Load(CodeGenContext context, AstOperation operation) + { + return GenerateLoadOrStore(context, operation, isStore: false); + } + + public static string Store(CodeGenContext context, AstOperation operation) + { + return GenerateLoadOrStore(context, operation, isStore: true); + } + + public static string TextureSample(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + + bool colorIsVector = isGather || !isShadow; + + string texCall = "texture."; + + int srcIndex = 0; + + string Src(AggregateType type) + { + return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); + } + + if (intCoords) + { + texCall += "read("; + } + else + { + texCall += "sample("; + + string samplerName = GetSamplerName(context.Properties, texOp); + + texCall += samplerName; + } + + int coordsCount = texOp.Type.GetDimensions(); + + int pCount = coordsCount; + + int arrayIndexElem = -1; + + if (isArray) + { + arrayIndexElem = pCount++; + } + + if (isShadow && !isGather) + { + pCount++; + } + + void Append(string str) + { + texCall += ", " + str; + } + + AggregateType coordType = intCoords ? AggregateType.S32 : AggregateType.FP32; + + string AssemblePVector(int count) + { + if (count > 1) + { + string[] elems = new string[count]; + + for (int index = 0; index < count; index++) + { + if (arrayIndexElem == index) + { + elems[index] = Src(AggregateType.S32); + + if (!intCoords) + { + elems[index] = "float(" + elems[index] + ")"; + } + } + else + { + elems[index] = Src(coordType); + } + } + + string prefix = intCoords ? "int" : "float"; + + return prefix + count + "(" + string.Join(", ", elems) + ")"; + } + else + { + return Src(coordType); + } + } + + Append(AssemblePVector(pCount)); + + texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); + + return texCall; + } + + private static string GetSamplerName(ShaderProperties resourceDefinitions, AstTextureOperation textOp) + { + return resourceDefinitions.Textures[textOp.Binding].Name; + } + + // TODO: Verify that this is valid in MSL + private static string GetMask(int index) + { + return $".{"rgba".AsSpan(index, 1)}"; + } + + private static string GetMaskMultiDest(int mask) + { + string swizzle = "."; + + for (int i = 0; i < 4; i++) + { + if ((mask & (1 << i)) != 0) + { + swizzle += "xyzw"[i]; + } + } + + return swizzle; + } + + public static string TextureSize(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + string textureName = "texture"; + string texCall = textureName + "."; + + if (texOp.Index == 3) + { + texCall += $"get_num_mip_levels()"; + } + else + { + context.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition); + bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; + texCall += "get_"; + + if (texOp.Index == 0) + { + texCall += "width"; + } + else if (texOp.Index == 1) + { + texCall += "height"; + } + else + { + texCall += "depth"; + } + + texCall += "("; + + if (hasLod) + { + IAstNode lod = operation.GetSource(0); + string lodExpr = GetSourceExpr(context, lod, GetSrcVarType(operation.Inst, 0)); + + texCall += $"{lodExpr}"; + } + + texCall += $"){GetMask(texOp.Index)}"; + } + + return texCall; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs new file mode 100644 index 000000000..9d8dae543 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs @@ -0,0 +1,32 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; + +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + static class InstGenVector + { + public static string VectorExtract(CodeGenContext context, AstOperation operation) + { + IAstNode vector = operation.GetSource(0); + IAstNode index = operation.GetSource(1); + + string vectorExpr = GetSourceExpr(context, vector, OperandManager.GetNodeDestType(context, vector)); + + if (index is AstOperand indexOperand && indexOperand.Type == OperandType.Constant) + { + char elem = "xyzw"[indexOperand.Value]; + + return $"{vectorExpr}.{elem}"; + } + else + { + string indexExpr = GetSourceExpr(context, index, GetSrcVarType(operation.Inst, 1)); + + return $"{vectorExpr}[{indexExpr}]"; + } + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 261a0e572..67e78c9f6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -1,11 +1,18 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; +using System.Globalization; namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { static class IoMap { - public static (string, AggregateType) GetMSLBuiltIn(IoVariable ioVariable) + public static (string, AggregateType) GetMslBuiltIn( + ShaderDefinitions definitions, + IoVariable ioVariable, + int location, + int component, + bool isOutput, + bool isPerPatch) { return ioVariable switch { @@ -18,12 +25,50 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.InstanceId => ("instance_id", AggregateType.S32), IoVariable.PointCoord => ("point_coord", AggregateType.Vector2), IoVariable.PointSize => ("point_size", AggregateType.FP32), - IoVariable.Position => ("position", AggregateType.Vector4), + IoVariable.Position => ("position", AggregateType.Vector4 | AggregateType.FP32), IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), + IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), IoVariable.VertexId => ("vertex_id", AggregateType.S32), IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), _ => (null, AggregateType.Invalid), }; } + + private static (string, AggregateType) GetUserDefinedVariableName(ShaderDefinitions definitions, int location, int component, bool isOutput, bool isPerPatch) + { + string name = isPerPatch + ? DefaultNames.PerPatchAttributePrefix + : (isOutput ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix); + + if (location < 0) + { + return (name, definitions.GetUserDefinedType(0, isOutput)); + } + + name += location.ToString(CultureInfo.InvariantCulture); + + if (definitions.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput)) + { + name += "_" + "xyzw"[component & 3]; + } + + string prefix = ""; + switch (definitions.Stage) + { + case ShaderStage.Vertex: + prefix = "Vertex"; + break; + case ShaderStage.Fragment: + prefix = "Fragment"; + break; + case ShaderStage.Compute: + prefix = "Compute"; + break; + } + + prefix += isOutput ? "Out" : "In"; + + return (prefix + "." + name, definitions.GetUserDefinedType(location, isOutput)); + } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index e0ce97abe..b4d2ecad2 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -90,10 +90,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl funcKeyword = "fragment"; funcName = "fragmentMain"; } + else if (stage == ShaderStage.Compute) + { + // TODO: Compute main + } if (context.AttributeUsage.UsedInputAttributes != 0) { - args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); + if (stage == ShaderStage.Vertex) + { + args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); + } + else if (stage == ShaderStage.Fragment) + { + args = args.Prepend("FragmentIn in [[stage_in]]").ToArray(); + } + else if (stage == ShaderStage.Compute) + { + // TODO: Compute input + } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs index beaf25f68..6d211b7e8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs @@ -46,9 +46,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node) { - // TODO: Get rid of that function entirely and return the type from the operation generation - // functions directly, like SPIR-V does. - if (node is AstOperation operation) { if (operation.Inst == Instruction.Load || operation.Inst.IsAtomic()) @@ -99,6 +96,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable ioVariable = (IoVariable)varId.Value; bool isOutput = operation.StorageKind == StorageKind.Output || operation.StorageKind == StorageKind.OutputPerPatch; bool isPerPatch = operation.StorageKind == StorageKind.InputPerPatch || operation.StorageKind == StorageKind.OutputPerPatch; + int location = 0; + int component = 0; if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) { @@ -107,18 +106,24 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); } - int location = vecIndex.Value; + location = vecIndex.Value; if (operation.SourcesCount > 2 && operation.GetSource(2) is AstOperand elemIndex && elemIndex.Type == OperandType.Constant && context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) { - int component = elemIndex.Value; + component = elemIndex.Value; } } - (_, AggregateType varType) = IoMap.GetMSLBuiltIn(ioVariable); + (_, AggregateType varType) = IoMap.GetMslBuiltIn( + context.Definitions, + ioVariable, + location, + component, + isOutput, + isPerPatch); return varType & AggregateType.ElementTypeMask; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index a579433f9..246867ba5 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Shader.CodeGen; using Ryujinx.Graphics.Shader.CodeGen.Glsl; +using Ryujinx.Graphics.Shader.CodeGen.Msl; using Ryujinx.Graphics.Shader.CodeGen.Spirv; using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; @@ -373,6 +374,7 @@ namespace Ryujinx.Graphics.Shader.Translation { TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, parameters)), TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, parameters)), + TargetLanguage.Msl => new ShaderProgram(info, TargetLanguage.Msl, MslGenerator.Generate(sInfo, parameters)), _ => throw new NotImplementedException(Options.TargetLanguage.ToString()), }; } -- 2.47.1 From 398b6cb60e48e4c00819fdc360a3d89b20efc968 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 30 Aug 2023 19:25:12 +0100 Subject: [PATCH 039/368] dotnet format --- src/Ryujinx.Headless.SDL2/Program.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index da5688a7a..fad45faee 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -515,7 +515,9 @@ namespace Ryujinx.Headless.SDL2 return options.GraphicsBackend switch { GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet), - GraphicsBackend.Metal => new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet), + GraphicsBackend.Metal => OperatingSystem.IsMacOS() ? + new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet) : + throw new Exception("Attempted to use Metal renderer on non-macOS platform!"), _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet) }; } -- 2.47.1 From 7182ac7233ed88830963595fda6f2792b984fee2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 30 Aug 2023 19:35:57 +0100 Subject: [PATCH 040/368] Get build working again (values likely wrong) --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 6d1a2f5e2..18f11e583 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -163,10 +163,12 @@ namespace Ryujinx.Graphics.Metal supportsMismatchingViewFormat: true, supportsCubemapView: true, supportsNonConstantTextureOffset: false, + supportsScaledVertexFormats: true, supportsShaderBallot: false, supportsShaderBarrierDivergence: false, supportsShaderFloat64: false, supportsTextureShadowLod: false, + supportsVertexStoreAndAtomics: false, supportsViewportIndexVertexTessellation: false, supportsViewportMask: false, supportsViewportSwizzle: false, @@ -178,7 +180,9 @@ namespace Ryujinx.Graphics.Metal maximumImagesPerStage: Constants.MaxTextureBindings, maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength, maximumSupportedAnisotropy: 0, + shaderSubgroupSize: 256, storageBufferOffsetAlignment: 0, + textureBufferOffsetAlignment: 0, gatherBiasPrecision: 0 ); } -- 2.47.1 From b06afd1a1f2628e7dcad87d8b378ff1bd60508f4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 30 Aug 2023 19:53:19 +0100 Subject: [PATCH 041/368] LDR ASTC --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 7b07cb4c7..6bacae0c0 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -127,7 +127,7 @@ namespace Ryujinx.Graphics.Metal // Add(Format.R10G10B10A2Sint, MTLPixelFormat.A2B10G10R10SintPack32); // Add(Format.R10G10B10A2Uscaled, MTLPixelFormat.A2B10G10R10UscaledPack32); // Add(Format.R10G10B10A2Sscaled, MTLPixelFormat.A2B10G10R10SscaledPack32); - Add(Format.Astc4x4Unorm, MTLPixelFormat.ASTC4x4HDR); + Add(Format.Astc4x4Unorm, MTLPixelFormat.ASTC4x4LDR); Add(Format.Astc5x4Unorm, MTLPixelFormat.ASTC5x4LDR); Add(Format.Astc5x5Unorm, MTLPixelFormat.ASTC5x5LDR); Add(Format.Astc6x5Unorm, MTLPixelFormat.ASTC6x5LDR); -- 2.47.1 From 9cc56a3bca4f183ab501ba183c5fb42886ba8b3f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 9 Oct 2023 11:12:04 -0400 Subject: [PATCH 042/368] Fix instructions --- .../CodeGen/Msl/Instructions/InstGen.cs | 6 ++++-- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 3 ++- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index ffb2105ea..030f3402e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -137,8 +137,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return Store(context, operation); case Instruction.TextureSample: return TextureSample(context, operation); - case Instruction.TextureSize: - return TextureSize(context, operation); + case Instruction.TextureQuerySamples: + return "|| TEXTURE QUERY SIZE ||"; + case Instruction.TextureQuerySize: + return TextureQuerySize(context, operation); case Instruction.VectorExtract: return VectorExtract(context, operation); case Instruction.VoteAllEqual: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 489787747..7991c942e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -119,7 +119,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.Subtract, InstType.OpBinary, "-", 2); Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); Add(Instruction.TextureSample, InstType.Special); - Add(Instruction.TextureSize, InstType.Special); + Add(Instruction.TextureQuerySamples, InstType.Special); + Add(Instruction.TextureQuerySize, InstType.Special); Add(Instruction.Truncate, InstType.CallUnary, "trunc"); Add(Instruction.UnpackDouble2x32, 0); // MSL does not have a 64-bit FP Add(Instruction.UnpackHalf2x16, InstType.CallUnary, "unpack_unorm2x16_to_half"); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 2f0434c96..613387843 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -269,7 +269,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return swizzle; } - public static string TextureSize(CodeGenContext context, AstOperation operation) + public static string TextureQuerySize(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; -- 2.47.1 From 4587905cd8367a7e7116f5626a583473efcac1d4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 9 Oct 2023 11:16:33 -0400 Subject: [PATCH 043/368] Partial TextureQuerySamples --- .../CodeGen/Msl/Instructions/InstGen.cs | 2 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 030f3402e..f4eb33516 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -138,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.TextureSample: return TextureSample(context, operation); case Instruction.TextureQuerySamples: - return "|| TEXTURE QUERY SIZE ||"; + return TextureQuerySamples(context, operation); case Instruction.TextureQuerySize: return TextureQuerySize(context, operation); case Instruction.VectorExtract: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 613387843..73936383c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -269,6 +269,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return swizzle; } + public static string TextureQuerySamples(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + + // TODO: Bindless texture support. For now we just return 0. + if (isBindless) + { + return NumberFormatter.FormatInt(0); + } + + string textureName = "texture"; + string texCall = textureName + "."; + texCall += $"get_num_samples()"; + + return texCall; + } + public static string TextureQuerySize(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; -- 2.47.1 From 2a28950739e588b5b63f8a914cacf27474986bf6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 9 Oct 2023 11:20:23 -0400 Subject: [PATCH 044/368] Fix ETC2 PTA formats Format --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 6bacae0c0..29f348cac 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -94,11 +94,11 @@ namespace Ryujinx.Graphics.Metal Add(Format.Bc6HSfloat, MTLPixelFormat.BC6HRGBFloat); Add(Format.Bc6HUfloat, MTLPixelFormat.BC6HRGBUfloat); Add(Format.Etc2RgbUnorm, MTLPixelFormat.ETC2RGB8); - Add(Format.Etc2RgbaUnorm, MTLPixelFormat.ETC2RGB8A1); - // Add(Format.Etc2RgbPtaUnorm, MTLPixelFormat.Etc2RgbPtaUnorm); + // Add(Format.Etc2RgbaUnorm, MTLPixelFormat.ETC2RGBA8); + Add(Format.Etc2RgbPtaUnorm, MTLPixelFormat.ETC2RGB8A1); Add(Format.Etc2RgbSrgb, MTLPixelFormat.ETC2RGB8sRGB); - Add(Format.Etc2RgbaSrgb, MTLPixelFormat.ETC2RGB8A1sRGB); - // Add(Format.Etc2RgbPtaSrgb, MTLPixelFormat.Etc2RgbPtaSrgb); + // Add(Format.Etc2RgbaSrgb, MTLPixelFormat.ETC2RGBA8sRGB); + Add(Format.Etc2RgbPtaSrgb, MTLPixelFormat.ETC2RGB8A1sRGB); // Add(Format.R8Uscaled, MTLPixelFormat.R8Uscaled); // Add(Format.R8Sscaled, MTLPixelFormat.R8Sscaled); // Add(Format.R16Uscaled, MTLPixelFormat.R16Uscaled); -- 2.47.1 From d65858be25b82df8656f29d46616a4597115438c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 9 Oct 2023 11:33:28 -0400 Subject: [PATCH 045/368] Fix IoMap variable names Output struct Lazy Vertex IO Output fixes Fix output struct definition MSL Binding Model description Might need tweaks/adjustments Cleanup Typo + Format --- .../CodeGen/Msl/Declarations.cs | 83 ++++++++++++++++++- .../CodeGen/Msl/Instructions/InstGen.cs | 4 + .../CodeGen/Msl/Instructions/IoMap.cs | 18 +--- .../CodeGen/Msl/MslGenerator.cs | 6 +- 4 files changed, 90 insertions(+), 21 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index f06af235a..8feba561b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -3,14 +3,41 @@ using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; -using System.Data.Common; using System.Linq; -using System.Numerics; namespace Ryujinx.Graphics.Shader.CodeGen.Msl { static class Declarations { + /* + * Description of MSL Binding Strategy + * + * There are a few fundamental differences between how GLSL and MSL handle I/O. + * This comment will set out to describe the reasons why things are done certain ways + * and to describe the overall binding model that we're striving for here. + * + * Main I/O Structs + * + * Each stage will have a main input and output struct labeled as [Stage][In/Out], i.e VertexIn. + * Every attribute within these structs will be labeled with an [[attribute(n)]] property, + * and the overall struct will be labeled with [[stage_in]] for input structs, and defined as the + * output type of the main shader function for the output struct. This struct also contains special + * attribute-based properties like [[position]], therefore these are not confined to 'user-defined' variables. + * + * Samplers & Textures + * + * Metal does not have a combined image sampler like sampler2D in GLSL, as a result we need to bind + * an individual texture and a sampler object for each instance of a combined image sampler. + * Therefore, the binding indices of straight up textures (i.e. without a sampler) must start + * after the last sampler/texture pair (n + Number of Pairs). + * + * Uniforms + * + * MSL does not have a concept of uniforms comparable to that of GLSL. As a result, instead of + * being declared outside of any function body, uniforms are part of the function signature in MSL. + * This applies to anything bound to the shader not included in the main I/O structs. + */ + public static void Declare(CodeGenContext context, StructuredProgramInfo info) { context.AppendLine("#include "); @@ -25,6 +52,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); + context.AppendLine(); + DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); } static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) @@ -32,8 +61,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return ioDefinition.StorageKind == storageKind && ioDefinition.IoVariable == IoVariable.UserDefined; } - public static void DeclareLocals(CodeGenContext context, StructuredFunction function) + public static void DeclareLocals(CodeGenContext context, StructuredFunction function, ShaderStage stage) { + if (stage == ShaderStage.Vertex) + { + context.AppendLine("VertexOutput out;"); + } + foreach (AstOperand decl in function.Locals) { string name = context.OperandManager.DeclareLocal(decl); @@ -107,5 +141,48 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } } + + private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable inputs) + { + if (context.Definitions.IaIndexing) + { + // Not handled + } + else + { + if (inputs.Any()) + { + string prefix = ""; + + switch (context.Definitions.Stage) + { + case ShaderStage.Vertex: + prefix = "Vertex"; + break; + case ShaderStage.Fragment: + prefix = "Fragment"; + break; + case ShaderStage.Compute: + prefix = "Compute"; + break; + } + + context.AppendLine($"struct {prefix}Output"); + context.EnterScope(); + + foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) + { + string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)); + string name = $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}"; + name = ioDefinition.IoVariable == IoVariable.Position ? "position" : name; + string suffix = ioDefinition.IoVariable == IoVariable.Position ? " [[position]]" : ""; + + context.AppendLine($"{type} {name}{suffix};"); + } + + context.LeaveScope(";"); + } + } + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index f4eb33516..5113c58c8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -70,6 +70,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { return $"{op} {GetSourceExpr(context, operation.GetSource(0), context.CurrentFunction.ReturnType)}"; } + else if (inst == Instruction.Return && context.Definitions.Stage == ShaderStage.Vertex) + { + return $"{op} out"; + } int arity = (int)(info.Type & InstType.ArityMask); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 67e78c9f6..c30e317b3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.InstanceId => ("instance_id", AggregateType.S32), IoVariable.PointCoord => ("point_coord", AggregateType.Vector2), IoVariable.PointSize => ("point_size", AggregateType.FP32), - IoVariable.Position => ("position", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), IoVariable.VertexId => ("vertex_id", AggregateType.S32), @@ -52,21 +52,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions name += "_" + "xyzw"[component & 3]; } - string prefix = ""; - switch (definitions.Stage) - { - case ShaderStage.Vertex: - prefix = "Vertex"; - break; - case ShaderStage.Fragment: - prefix = "Fragment"; - break; - case ShaderStage.Compute: - prefix = "Compute"; - break; - } - - prefix += isOutput ? "Out" : "In"; + string prefix = isOutput ? "out" : "in"; return (prefix + "." + name, definitions.GetUserDefinedType(location, isOutput)); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index b4d2ecad2..bd03aeeb5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(GetFunctionSignature(context, function, stage, isMainFunc)); context.EnterScope(); - Declarations.DeclareLocals(context, function); + Declarations.DeclareLocals(context, function, stage); PrintBlock(context, function.MainBlock, isMainFunc); @@ -77,6 +77,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl string funcKeyword = "inline"; string funcName = null; + string returnType = Declarations.GetVarTypeName(context, function.ReturnType); if (isMainFunc) { @@ -84,6 +85,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { funcKeyword = "vertex"; funcName = "vertexMain"; + returnType = "VertexOutput"; } else if (stage == ShaderStage.Fragment) { @@ -112,7 +114,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } - return $"{funcKeyword} {Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})"; + return $"{funcKeyword} {returnType} {funcName ?? function.Name}({string.Join(", ", args)})"; } private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction) -- 2.47.1 From 2e3509f8e8c3da03c70832c08d567777909867eb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 13:26:49 -0400 Subject: [PATCH 046/368] Set TargetLanguage for Metal to MSL --- src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 9 ++++++--- src/Ryujinx.Graphics.Metal/Program.cs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 4fc66c4c0..604c97200 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -829,9 +829,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Translation options private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags) { - TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan - ? TargetLanguage.Spirv - : TargetLanguage.Glsl; + TargetLanguage lang = api switch + { + TargetApi.OpenGL => TargetLanguage.Glsl, + TargetApi.Vulkan => GraphicsConfig.EnableSpirvCompilationOnVulkan ? TargetLanguage.Spirv : TargetLanguage.Glsl, + TargetApi.Metal => TargetLanguage.Msl, + }; return new TranslationOptions(lang, api, flags); } diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 0a748bfcf..a7953dde1 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Metal { public ProgramLinkStatus CheckProgramLink(bool blocking) { - return ProgramLinkStatus.Failure; + return ProgramLinkStatus.Success; } public byte[] GetBinary() -- 2.47.1 From fe62c794b9f313cd3a2091891c5dd34cabb8ea00 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 13:43:57 -0400 Subject: [PATCH 047/368] Fix fragment output color --- src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 604c97200..c67c6a2d6 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -822,7 +822,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Creates shader translation options with the requested graphics API and flags. - /// The shader language is choosen based on the current configuration and graphics API. + /// The shader language is chosen based on the current configuration and graphics API. /// /// Target graphics API /// Translation flags diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index c30e317b3..5c7800de8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.BaseInstance => ("base_instance", AggregateType.S32), IoVariable.BaseVertex => ("base_vertex", AggregateType.S32), IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), - IoVariable.FragmentOutputColor => ("color", AggregateType.Vector2 | AggregateType.Vector3 | AggregateType.Vector4), + IoVariable.FragmentOutputColor => ("out.color", AggregateType.Vector4 | AggregateType.FP32), IoVariable.FragmentOutputDepth => ("depth", AggregateType.FP32), IoVariable.FrontFacing => ("front_facing", AggregateType.Bool), IoVariable.InstanceId => ("instance_id", AggregateType.S32), -- 2.47.1 From 3398977c97252348c1c0e2502c7092da2b116454 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 14:14:28 -0400 Subject: [PATCH 048/368] Implement CreateProgram --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 3 +- src/Ryujinx.Graphics.Metal/Program.cs | 49 ++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 18f11e583..5ecaf7575 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -74,8 +74,7 @@ namespace Ryujinx.Graphics.Metal public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - return new Program(); + return new Program(shaders, _device); } public ISampler CreateSampler(SamplerCreateInfo info) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index a7953dde1..6cf2a96e5 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -1,12 +1,59 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + [SupportedOSPlatform("macos")] class Program : IProgram { + private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete; + private MTLFunction[] _shaderHandles; + + public Program(ShaderSource[] shaders, MTLDevice device) + { + _shaderHandles = new MTLFunction[shaders.Length]; + + for (int index = 0; index < shaders.Length; index++) + { + var libraryError = new NSError(IntPtr.Zero); + ShaderSource shader = shaders[index]; + var shaderLibrary = device.NewLibrary(StringHelper.NSString(shader.Code), new MTLCompileOptions(IntPtr.Zero), ref libraryError); + if (libraryError != IntPtr.Zero) + { + Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); + _status = ProgramLinkStatus.Failure; + } + else + { + switch (shaders[index].Stage) + { + case ShaderStage.Compute: + _shaderHandles[index] = shaderLibrary.NewFunction(StringHelper.NSString("computeMain")); + break; + case ShaderStage.Vertex: + _shaderHandles[index] = shaderLibrary.NewFunction(StringHelper.NSString("vertexMain")); + break; + case ShaderStage.Fragment: + _shaderHandles[index] = shaderLibrary.NewFunction(StringHelper.NSString("fragmentMain")); + break; + default: + Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shaders[index].Stage}!"); + break; + } + + _status = ProgramLinkStatus.Success; + } + } + } + public ProgramLinkStatus CheckProgramLink(bool blocking) { - return ProgramLinkStatus.Success; + return _status; } public byte[] GetBinary() -- 2.47.1 From 3529fcd59235f138b2eb232f65c559c64ff83898 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 15:26:40 -0400 Subject: [PATCH 049/368] Start vertex descriptor work --- src/Ryujinx.Graphics.Metal/HelperShaders.cs | 24 ++------ src/Ryujinx.Graphics.Metal/Pipeline.cs | 14 ++++- src/Ryujinx.Graphics.Metal/Program.cs | 12 ++-- .../RenderEncoderState.cs | 60 +++++++++++++++++-- 4 files changed, 76 insertions(+), 34 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShaders.cs b/src/Ryujinx.Graphics.Metal/HelperShaders.cs index a4517b7ce..7c1eada7b 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShaders.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShaders.cs @@ -32,29 +32,13 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public readonly struct HelperShader { - private readonly MTLRenderPipelineState _pipelineState; - public static implicit operator MTLRenderPipelineState(HelperShader shader) => shader._pipelineState; + public readonly MTLFunction VertexFunction; + public readonly MTLFunction FragmentFunction; public HelperShader(MTLDevice device, MTLLibrary library, string vertex, string fragment) { - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor - { - VertexFunction = library.NewFunction(StringHelper.NSString(vertex)), - FragmentFunction = library.NewFunction(StringHelper.NSString(fragment)) - }; - renderPipelineDescriptor.ColorAttachments.Object(0).SetBlendingEnabled(true); - renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; - renderPipelineDescriptor.ColorAttachments.Object(0).SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - - var error = new NSError(IntPtr.Zero); - _pipelineState = device.NewRenderPipelineState(renderPipelineDescriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } + VertexFunction = library.NewFunction(StringHelper.NSString(vertex)); + FragmentFunction = library.NewFunction(StringHelper.NSString(fragment)); } } } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 2c1e0d4e3..93f463418 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -40,7 +40,10 @@ namespace Ryujinx.Graphics.Metal _commandQueue = commandQueue; _helperShaders = new HelperShaders(_device); - _renderEncoderState = new RenderEncoderState(_helperShaders.BlitShader, _device); + _renderEncoderState = new RenderEncoderState( + _helperShaders.BlitShader.VertexFunction, + _helperShaders.BlitShader.FragmentFunction, + _device); _commandBuffer = _commandQueue.CommandBuffer(); @@ -424,7 +427,12 @@ namespace Ryujinx.Graphics.Metal public void SetProgram(IProgram program) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + Program prg = (Program)program; + + _renderEncoderState = new RenderEncoderState( + prg.VertexFunction, + prg.FragmentFunction, + _device); } public void SetRasterizerDiscard(bool discard) @@ -523,7 +531,7 @@ namespace Ryujinx.Graphics.Metal public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _renderEncoderState.UpdateVertexDescriptor(vertexBuffers); } public unsafe void SetViewports(ReadOnlySpan viewports) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 6cf2a96e5..7a7eb5836 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Graphics.Metal class Program : IProgram { private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete; - private MTLFunction[] _shaderHandles; + public MTLFunction VertexFunction; + public MTLFunction FragmentFunction; + public MTLFunction ComputeFunction; public Program(ShaderSource[] shaders, MTLDevice device) { - _shaderHandles = new MTLFunction[shaders.Length]; - for (int index = 0; index < shaders.Length; index++) { var libraryError = new NSError(IntPtr.Zero); @@ -33,13 +33,13 @@ namespace Ryujinx.Graphics.Metal switch (shaders[index].Stage) { case ShaderStage.Compute: - _shaderHandles[index] = shaderLibrary.NewFunction(StringHelper.NSString("computeMain")); + ComputeFunction = shaderLibrary.NewFunction(StringHelper.NSString("computeMain")); break; case ShaderStage.Vertex: - _shaderHandles[index] = shaderLibrary.NewFunction(StringHelper.NSString("vertexMain")); + VertexFunction = shaderLibrary.NewFunction(StringHelper.NSString("vertexMain")); break; case ShaderStage.Fragment: - _shaderHandles[index] = shaderLibrary.NewFunction(StringHelper.NSString("fragmentMain")); + FragmentFunction = shaderLibrary.NewFunction(StringHelper.NSString("fragmentMain")); break; default: Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shaders[index].Stage}!"); diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index e24b49090..c394ba385 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -1,4 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using SharpMetal.Foundation; using SharpMetal.Metal; using System; using System.Runtime.Versioning; @@ -9,8 +11,8 @@ namespace Ryujinx.Graphics.Metal struct RenderEncoderState { private readonly MTLDevice _device; - // TODO: Work with more than one pipeline state - private readonly MTLRenderPipelineState _copyPipeline; + private readonly MTLFunction _vertexFunction = null; + private readonly MTLFunction _fragmentFunction = null; private MTLDepthStencilState _depthStencilState = null; private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always; @@ -19,19 +21,51 @@ namespace Ryujinx.Graphics.Metal private MTLStencilDescriptor _backFaceStencil = null; private MTLStencilDescriptor _frontFaceStencil = null; + private MTLVertexDescriptor _vertexDescriptor = new(); + public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.Clockwise; - public RenderEncoderState(MTLRenderPipelineState copyPipeline, MTLDevice device) + public RenderEncoderState(MTLFunction vertexFunction, MTLFunction fragmentFunction, MTLDevice device) { + _vertexFunction = vertexFunction; + _fragmentFunction = fragmentFunction; _device = device; - _copyPipeline = copyPipeline; } public readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder) { - renderCommandEncoder.SetRenderPipelineState(_copyPipeline); + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor + { + VertexDescriptor = _vertexDescriptor + }; + + if (_vertexFunction != null) + { + renderPipelineDescriptor.VertexFunction = _vertexFunction; + } + + if (_fragmentFunction != null) + { + renderPipelineDescriptor.VertexFunction = _fragmentFunction; + } + + renderPipelineDescriptor.ColorAttachments.Object(0).SetBlendingEnabled(true); + renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; + renderPipelineDescriptor.ColorAttachments.Object(0).SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + renderPipelineDescriptor.ColorAttachments.Object(0).DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + + var error = new NSError(IntPtr.Zero); + var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + renderCommandEncoder.SetRenderPipelineState(pipelineState); renderCommandEncoder.SetCullMode(CullMode); renderCommandEncoder.SetFrontFacingWinding(Winding); @@ -68,5 +102,21 @@ namespace Ryujinx.Graphics.Metal FrontFaceStencil = _frontFaceStencil }); } + + public void UpdateVertexDescriptor(ReadOnlySpan vertexBuffers) + { + _vertexDescriptor = new(); + + for (int i = 0; i < vertexBuffers.Length; i++) + { + if (vertexBuffers[i].Stride != 0) + { + // TODO: Format should not be hardcoded + _vertexDescriptor.Attributes.Object((ulong)i).Format = MTLVertexFormat.Float4; + _vertexDescriptor.Attributes.Object((ulong)i).BufferIndex = (ulong)i; + _vertexDescriptor.Layouts.Object((ulong)i).Stride = (ulong)vertexBuffers[i].Stride; + } + } + } } } -- 2.47.1 From be1d0998790fc5b9812e25e224401fea44f6d737 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 17:53:51 -0400 Subject: [PATCH 050/368] Set Vertex Descriptor properly --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 ++-- .../RenderEncoderState.cs | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 93f463418..ad94c9aee 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -526,12 +526,12 @@ namespace Ryujinx.Graphics.Metal public void SetVertexAttribs(ReadOnlySpan vertexAttribs) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _renderEncoderState.UpdateVertexAttributes(vertexAttribs); } public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - _renderEncoderState.UpdateVertexDescriptor(vertexBuffers); + _renderEncoderState.UpdateVertexBuffers(vertexBuffers); } public unsafe void SetViewports(ReadOnlySpan viewports) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index c394ba385..74254c66f 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -103,17 +103,26 @@ namespace Ryujinx.Graphics.Metal }); } - public void UpdateVertexDescriptor(ReadOnlySpan vertexBuffers) + public void UpdateVertexAttributes(ReadOnlySpan vertexAttribs) { + // Reset Vertex Descriptor _vertexDescriptor = new(); + for (int i = 0; i < vertexAttribs.Length; i++) + { + // TODO: Format should not be hardcoded + _vertexDescriptor.Attributes.Object((ulong)i).Format = MTLVertexFormat.Float4; + _vertexDescriptor.Attributes.Object((ulong)i).BufferIndex = (ulong)vertexAttribs[i].BufferIndex; + _vertexDescriptor.Attributes.Object((ulong)i).Offset = (ulong)vertexAttribs[i].Offset; + } + } + + public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) + { for (int i = 0; i < vertexBuffers.Length; i++) { if (vertexBuffers[i].Stride != 0) { - // TODO: Format should not be hardcoded - _vertexDescriptor.Attributes.Object((ulong)i).Format = MTLVertexFormat.Float4; - _vertexDescriptor.Attributes.Object((ulong)i).BufferIndex = (ulong)i; _vertexDescriptor.Layouts.Object((ulong)i).Stride = (ulong)vertexBuffers[i].Stride; } } -- 2.47.1 From a5c1b6a255567544e45c58b708f6eba3bff947be Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 18:00:32 -0400 Subject: [PATCH 051/368] Reset Descriptor instead of making a new object --- src/Ryujinx.Graphics.Metal/RenderEncoderState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 74254c66f..c712c96f3 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateVertexAttributes(ReadOnlySpan vertexAttribs) { // Reset Vertex Descriptor - _vertexDescriptor = new(); + _vertexDescriptor.Reset(); for (int i = 0; i < vertexAttribs.Length; i++) { -- 2.47.1 From 02fbcfbadbdc396608e3ea6c337d368e08901e0d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 18:04:26 -0400 Subject: [PATCH 052/368] Dont set 0 attributes --- src/Ryujinx.Graphics.Metal/RenderEncoderState.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index c712c96f3..82a2b1e42 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -105,15 +105,15 @@ namespace Ryujinx.Graphics.Metal public void UpdateVertexAttributes(ReadOnlySpan vertexAttribs) { - // Reset Vertex Descriptor - _vertexDescriptor.Reset(); - for (int i = 0; i < vertexAttribs.Length; i++) { - // TODO: Format should not be hardcoded - _vertexDescriptor.Attributes.Object((ulong)i).Format = MTLVertexFormat.Float4; - _vertexDescriptor.Attributes.Object((ulong)i).BufferIndex = (ulong)vertexAttribs[i].BufferIndex; - _vertexDescriptor.Attributes.Object((ulong)i).Offset = (ulong)vertexAttribs[i].Offset; + if (!vertexAttribs[i].IsZero) + { + // TODO: Format should not be hardcoded + _vertexDescriptor.Attributes.Object((ulong)i).Format = MTLVertexFormat.Float4; + _vertexDescriptor.Attributes.Object((ulong)i).BufferIndex = (ulong)vertexAttribs[i].BufferIndex; + _vertexDescriptor.Attributes.Object((ulong)i).Offset = (ulong)vertexAttribs[i].Offset; + } } } -- 2.47.1 From c216028d00c4b6829bc68d968a5d3647ceda7d59 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 18:36:52 -0400 Subject: [PATCH 053/368] Dont be stupid --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 25 +++++++++-- .../RenderEncoderState.cs | 44 ++++--------------- 2 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index ad94c9aee..b71266c04 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.Metal private MTLCommandEncoder _currentEncoder; private RenderEncoderState _renderEncoderState; + private MTLVertexDescriptor _vertexDescriptor = new(); private MTLBuffer _indexBuffer; private MTLIndexType _indexType; @@ -117,7 +118,7 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLRenderPassDescriptor(); var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); - _renderEncoderState.SetEncoderState(renderCommandEncoder); + _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); _currentEncoder = renderCommandEncoder; return renderCommandEncoder; @@ -160,7 +161,7 @@ namespace Ryujinx.Graphics.Metal descriptor.ColorAttachments.Object(0).ClearColor = _clearColor; var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); - _renderEncoderState.SetEncoderState(renderCommandEncoder); + _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); var sampler = _device.NewSamplerState(new MTLSamplerDescriptor { @@ -526,12 +527,28 @@ namespace Ryujinx.Graphics.Metal public void SetVertexAttribs(ReadOnlySpan vertexAttribs) { - _renderEncoderState.UpdateVertexAttributes(vertexAttribs); + for (int i = 0; i < vertexAttribs.Length; i++) + { + if (!vertexAttribs[i].IsZero) + { + // TODO: Format should not be hardcoded + var attrib = _vertexDescriptor.Attributes.Object((ulong)i); + attrib.Format = MTLVertexFormat.Float4; + attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; + attrib.Offset = (ulong)vertexAttribs[i].Offset; + } + } } public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - _renderEncoderState.UpdateVertexBuffers(vertexBuffers); + for (int i = 0; i < vertexBuffers.Length; i++) + { + if (vertexBuffers[i].Stride != 0) + { + _vertexDescriptor.Layouts.Object((ulong)i).Stride = (ulong)vertexBuffers[i].Stride; + } + } } public unsafe void SetViewports(ReadOnlySpan viewports) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 82a2b1e42..3d54c7abc 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -21,8 +21,6 @@ namespace Ryujinx.Graphics.Metal private MTLStencilDescriptor _backFaceStencil = null; private MTLStencilDescriptor _frontFaceStencil = null; - private MTLVertexDescriptor _vertexDescriptor = new(); - public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.Clockwise; @@ -34,11 +32,11 @@ namespace Ryujinx.Graphics.Metal _device = device; } - public readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder) + public readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLVertexDescriptor vertexDescriptor) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor { - VertexDescriptor = _vertexDescriptor + VertexDescriptor = vertexDescriptor }; if (_vertexFunction != null) @@ -51,12 +49,13 @@ namespace Ryujinx.Graphics.Metal renderPipelineDescriptor.VertexFunction = _fragmentFunction; } - renderPipelineDescriptor.ColorAttachments.Object(0).SetBlendingEnabled(true); - renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; - renderPipelineDescriptor.ColorAttachments.Object(0).SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - renderPipelineDescriptor.ColorAttachments.Object(0).DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + var attachment = renderPipelineDescriptor.ColorAttachments.Object(0); + attachment.SetBlendingEnabled(true); + attachment.PixelFormat = MTLPixelFormat.BGRA8Unorm; + attachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + attachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + attachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + attachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; var error = new NSError(IntPtr.Zero); var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); @@ -102,30 +101,5 @@ namespace Ryujinx.Graphics.Metal FrontFaceStencil = _frontFaceStencil }); } - - public void UpdateVertexAttributes(ReadOnlySpan vertexAttribs) - { - for (int i = 0; i < vertexAttribs.Length; i++) - { - if (!vertexAttribs[i].IsZero) - { - // TODO: Format should not be hardcoded - _vertexDescriptor.Attributes.Object((ulong)i).Format = MTLVertexFormat.Float4; - _vertexDescriptor.Attributes.Object((ulong)i).BufferIndex = (ulong)vertexAttribs[i].BufferIndex; - _vertexDescriptor.Attributes.Object((ulong)i).Offset = (ulong)vertexAttribs[i].Offset; - } - } - } - - public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) - { - for (int i = 0; i < vertexBuffers.Length; i++) - { - if (vertexBuffers[i].Stride != 0) - { - _vertexDescriptor.Layouts.Object((ulong)i).Stride = (ulong)vertexBuffers[i].Stride; - } - } - } } } -- 2.47.1 From b3629e3a8baeb3d233d8d8a3ceb606e0e2dbeeac Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 19:02:38 -0400 Subject: [PATCH 054/368] Vertex buffer data --- src/Ryujinx.Graphics.Metal/Handle.cs | 14 ++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 15 +++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/Ryujinx.Graphics.Metal/Handle.cs diff --git a/src/Ryujinx.Graphics.Metal/Handle.cs b/src/Ryujinx.Graphics.Metal/Handle.cs new file mode 100644 index 000000000..82f8dbd5b --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Handle.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Graphics.Metal +{ + static class Handle + { + public static IntPtr ToIntPtr(this BufferHandle handle) + { + return Unsafe.As(ref handle); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index b71266c04..3a92d3435 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Metal private RenderEncoderState _renderEncoderState; private MTLVertexDescriptor _vertexDescriptor = new(); + private MTLBuffer[] _vertexBuffers; private MTLBuffer _indexBuffer; private MTLIndexType _indexType; @@ -120,6 +121,14 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); + for (int i = 0; i < _vertexBuffers.Length; i++) + { + if (_vertexBuffers[i] != null) + { + renderCommandEncoder.SetVertexBuffer(_vertexBuffers[i], 0, (ulong)i); + } + } + _currentEncoder = renderCommandEncoder; return renderCommandEncoder; } @@ -542,11 +551,17 @@ namespace Ryujinx.Graphics.Metal public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { + _vertexBuffers = new MTLBuffer[vertexBuffers.Length]; + for (int i = 0; i < vertexBuffers.Length; i++) { if (vertexBuffers[i].Stride != 0) { _vertexDescriptor.Layouts.Object((ulong)i).Stride = (ulong)vertexBuffers[i].Stride; + _vertexBuffers[i] = _device.NewBuffer( + vertexBuffers[i].Buffer.Handle.ToIntPtr(), + (ulong)vertexBuffers[i].Buffer.Size, + MTLResourceOptions.ResourceStorageModeManaged); } } } -- 2.47.1 From d45c7711ba83d9137b6bb37af884e2b23057d10c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 20:00:56 -0400 Subject: [PATCH 055/368] Fix fragment shaders (and fuck everything up) --- src/Ryujinx.Graphics.Metal/Program.cs | 3 ++- .../CodeGen/Msl/Declarations.cs | 21 +++++++++++++++---- .../CodeGen/Msl/Instructions/InstGen.cs | 2 +- .../CodeGen/Msl/MslGenerator.cs | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 7a7eb5836..ee3ff493f 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -20,8 +20,9 @@ namespace Ryujinx.Graphics.Metal { for (int index = 0; index < shaders.Length; index++) { - var libraryError = new NSError(IntPtr.Zero); ShaderSource shader = shaders[index]; + + var libraryError = new NSError(IntPtr.Zero); var shaderLibrary = device.NewLibrary(StringHelper.NSString(shader.Code), new MTLCompileOptions(IntPtr.Zero), ref libraryError); if (libraryError != IntPtr.Zero) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 8feba561b..956483a45 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -67,6 +67,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { context.AppendLine("VertexOutput out;"); } + else if (stage == ShaderStage.Fragment) + { + context.AppendLine("FragmentOutput out;"); + } foreach (AstOperand decl in function.Locals) { @@ -133,8 +137,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)); string name = $"{DefaultNames.IAttributePrefix}{ioDefinition.Location}"; + string suffix = context.Definitions.Stage == ShaderStage.Vertex ? $" [[attribute({ioDefinition.Location})]]" : ""; - context.AppendLine($"{type} {name} [[attribute({ioDefinition.Location})]];"); + context.AppendLine($"{type} {name}{suffix};"); } context.LeaveScope(";"); @@ -173,9 +178,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)); - string name = $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}"; - name = ioDefinition.IoVariable == IoVariable.Position ? "position" : name; - string suffix = ioDefinition.IoVariable == IoVariable.Position ? " [[position]]" : ""; + string name = ioDefinition.IoVariable switch + { + IoVariable.Position => "position", + IoVariable.FragmentOutputColor => "color", + _ => $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}" + }; + string suffix = ioDefinition.IoVariable switch + { + IoVariable.Position => " [[position]]", + _ => "" + }; context.AppendLine($"{type} {name}{suffix};"); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 5113c58c8..f18b34597 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -70,7 +70,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { return $"{op} {GetSourceExpr(context, operation.GetSource(0), context.CurrentFunction.ReturnType)}"; } - else if (inst == Instruction.Return && context.Definitions.Stage == ShaderStage.Vertex) + if (inst == Instruction.Return && context.Definitions.Stage is ShaderStage.Vertex or ShaderStage.Fragment) { return $"{op} out"; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index bd03aeeb5..0196164e8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -91,6 +91,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { funcKeyword = "fragment"; funcName = "fragmentMain"; + returnType = "FragmentOutput"; } else if (stage == ShaderStage.Compute) { -- 2.47.1 From 037157135ea48e35159636a9a25f7522bbb41d04 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 21:56:08 -0400 Subject: [PATCH 056/368] If one shader fails, whole program fails --- src/Ryujinx.Graphics.Metal/Program.cs | 35 +++++++++++++-------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index ee3ff493f..2b4c5f225 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -28,28 +28,27 @@ namespace Ryujinx.Graphics.Metal { Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); _status = ProgramLinkStatus.Failure; + return; } - else - { - switch (shaders[index].Stage) - { - case ShaderStage.Compute: - ComputeFunction = shaderLibrary.NewFunction(StringHelper.NSString("computeMain")); - break; - case ShaderStage.Vertex: - VertexFunction = shaderLibrary.NewFunction(StringHelper.NSString("vertexMain")); - break; - case ShaderStage.Fragment: - FragmentFunction = shaderLibrary.NewFunction(StringHelper.NSString("fragmentMain")); - break; - default: - Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shaders[index].Stage}!"); - break; - } - _status = ProgramLinkStatus.Success; + switch (shaders[index].Stage) + { + case ShaderStage.Compute: + ComputeFunction = shaderLibrary.NewFunction(StringHelper.NSString("computeMain")); + break; + case ShaderStage.Vertex: + VertexFunction = shaderLibrary.NewFunction(StringHelper.NSString("vertexMain")); + break; + case ShaderStage.Fragment: + FragmentFunction = shaderLibrary.NewFunction(StringHelper.NSString("fragmentMain")); + break; + default: + Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shaders[index].Stage}!"); + break; } } + + _status = ProgramLinkStatus.Success; } public ProgramLinkStatus CheckProgramLink(bool blocking) -- 2.47.1 From 5d909322775f235fbee9fc498b282761787f011b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 22:03:10 -0400 Subject: [PATCH 057/368] Dont specify [[stage_in]] on fragment --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 0196164e8..94801b96b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else if (stage == ShaderStage.Fragment) { - args = args.Prepend("FragmentIn in [[stage_in]]").ToArray(); + args = args.Prepend("FragmentIn in").ToArray(); } else if (stage == ShaderStage.Compute) { -- 2.47.1 From fbcd9994c87c5f965f7bc85ee25fcb9757eab4f8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 22:10:28 -0400 Subject: [PATCH 058/368] smh --- src/Ryujinx.Graphics.Metal/RenderEncoderState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 3d54c7abc..78013833c 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Metal if (_fragmentFunction != null) { - renderPipelineDescriptor.VertexFunction = _fragmentFunction; + renderPipelineDescriptor.FragmentFunction = _fragmentFunction; } var attachment = renderPipelineDescriptor.ColorAttachments.Object(0); -- 2.47.1 From b6116da9404375b96b8fb46b993d8421dbd9eb19 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 11 Oct 2023 00:39:18 -0400 Subject: [PATCH 059/368] Formatting --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- src/Ryujinx.Graphics.Metal/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 3a92d3435..240bccdc6 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Metal private MTLCommandEncoder _currentEncoder; private RenderEncoderState _renderEncoderState; - private MTLVertexDescriptor _vertexDescriptor = new(); + private readonly MTLVertexDescriptor _vertexDescriptor = new(); private MTLBuffer[] _vertexBuffers; private MTLBuffer _indexBuffer; diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 2b4c5f225..c666172cb 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Program : IProgram { - private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete; + private ProgramLinkStatus _status; public MTLFunction VertexFunction; public MTLFunction FragmentFunction; public MTLFunction ComputeFunction; -- 2.47.1 From c4cf4895d8aaeeaa060a363075a1c0dcbdf8dff9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 11 Oct 2023 00:42:38 -0400 Subject: [PATCH 060/368] format --- src/Ryujinx.Graphics.Metal/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index c666172cb..764bcf126 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Program : IProgram { - private ProgramLinkStatus _status; + private readonly ProgramLinkStatus _status; public MTLFunction VertexFunction; public MTLFunction FragmentFunction; public MTLFunction ComputeFunction; -- 2.47.1 From cb36036faaddd4c1e6723e5cee6c733e03766525 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 11 Oct 2023 20:19:28 -0400 Subject: [PATCH 061/368] Render Targets --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 240bccdc6..12b54bee9 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -24,10 +24,11 @@ namespace Ryujinx.Graphics.Metal private MTLCommandBuffer _commandBuffer; private MTLCommandEncoder _currentEncoder; + private MTLTexture[] _renderTargets = Array.Empty(); private RenderEncoderState _renderEncoderState; private readonly MTLVertexDescriptor _vertexDescriptor = new(); - private MTLBuffer[] _vertexBuffers; + private MTLBuffer[] _vertexBuffers = Array.Empty(); private MTLBuffer _indexBuffer; private MTLIndexType _indexType; @@ -118,6 +119,15 @@ namespace Ryujinx.Graphics.Metal EndCurrentPass(); var descriptor = new MTLRenderPassDescriptor(); + for (int i = 0; i < _renderTargets.Length; i++) + { + if (_renderTargets[i] != null) + { + var attachment = descriptor.ColorAttachments.Object((ulong)i); + attachment.Texture = _renderTargets[i]; + } + } + var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); @@ -457,7 +467,23 @@ namespace Ryujinx.Graphics.Metal public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _renderTargets = new MTLTexture[colors.Length]; + + for (int i = 0; i < colors.Length; i++) + { + if (colors[i] is not Texture tex) + { + continue; + } + + if (tex.MTLTexture != null) + { + _renderTargets[i] = tex.MTLTexture; + } + } + + // Recreate Render Command Encoder + BeginRenderPass(); } public unsafe void SetScissors(ReadOnlySpan> regions) -- 2.47.1 From dff9046f5578cd71dbf0a93f705cfff55290c938 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 14 Jan 2024 12:33:59 -0500 Subject: [PATCH 062/368] Get it building again --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 12 ++++++++++++ .../Ryujinx.Graphics.Metal.csproj | 6 +++--- src/Ryujinx/AppHost.cs | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 5ecaf7575..9b9d297cc 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -52,6 +52,11 @@ namespace Ryujinx.Graphics.Metal return CreateBuffer(size, BufferAccess.Default); } + public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint) + { + throw new NotImplementedException(); + } + public BufferHandle CreateBuffer(IntPtr pointer, int size) { var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); @@ -59,6 +64,11 @@ namespace Ryujinx.Graphics.Metal return Unsafe.As(ref bufferPtr); } + public BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers) + { + throw new NotImplementedException(); + } + public BufferHandle CreateBuffer(int size, BufferAccess access) { var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); @@ -150,6 +160,7 @@ namespace Ryujinx.Graphics.Metal supportsR4G4Format: false, supportsR4G4B4A4Format: true, supportsSnormBufferTextureFormat: true, + supportsSparseBuffer: false, supports5BitComponentFormat: true, supportsBlendEquationAdvanced: false, supportsFragmentShaderInterlock: true, @@ -166,6 +177,7 @@ namespace Ryujinx.Graphics.Metal supportsShaderBallot: false, supportsShaderBarrierDivergence: false, supportsShaderFloat64: false, + supportsTextureGatherOffsets: false, supportsTextureShadowLod: false, supportsVertexStoreAndAtomics: false, supportsViewportIndexVertexTessellation: false, diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 529666b9d..46f2d070d 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 true @@ -9,7 +9,7 @@ - + @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 5747b450e..31a81193a 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1149,7 +1149,7 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( vSyncMode, LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", - ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToText(), + ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToString(), dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), $"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", -- 2.47.1 From e7197877a2616742655388fe3f10b7f96fbe244b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 14 Jan 2024 16:50:05 -0500 Subject: [PATCH 063/368] Adjust function signature --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 9b9d297cc..1fdcad92d 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -47,14 +47,9 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public BufferHandle CreateBuffer(int size, BufferHandle storageHint) - { - return CreateBuffer(size, BufferAccess.Default); - } - public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint) { - throw new NotImplementedException(); + return CreateBuffer(size, access); } public BufferHandle CreateBuffer(IntPtr pointer, int size) -- 2.47.1 From e8d0212ec63a59de1ceb0c27cab7c6b49eed4799 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jan 2024 16:08:57 -0500 Subject: [PATCH 064/368] =?UTF-8?q?=E2=80=9CReport=E2=80=9D=20Driver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 1fdcad92d..a58b7cb60 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -201,7 +201,7 @@ namespace Ryujinx.Graphics.Metal public HardwareInfo GetHardwareInfo() { - return new HardwareInfo(HardwareInfoTools.GetVendor(), HardwareInfoTools.GetModel()); + return new HardwareInfo(HardwareInfoTools.GetVendor(), HardwareInfoTools.GetModel(), "Apple"); } public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) -- 2.47.1 From 26ea1e6d376952bea4140ba8f4af0897a04864db Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jan 2024 16:09:16 -0500 Subject: [PATCH 065/368] =?UTF-8?q?Don=E2=80=99t=20change=20Render=20State?= =?UTF-8?q?=20if=20Vertex=20Function=20is=20Invalid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 12b54bee9..4a32136d2 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -449,6 +449,12 @@ namespace Ryujinx.Graphics.Metal { Program prg = (Program)program; + if (prg.VertexFunction == null) + { + Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!"); + return; + } + _renderEncoderState = new RenderEncoderState( prg.VertexFunction, prg.FragmentFunction, -- 2.47.1 From df0dc4454b08b95a0d5f6aaa451ff584861633f2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jan 2024 16:09:24 -0500 Subject: [PATCH 066/368] End Pass on Dispose --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 4a32136d2..16e678710 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -671,7 +671,7 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { - + EndCurrentPass(); } } } -- 2.47.1 From 3c562d8906e2c2b4d39ec0257489c4d4ea71b996 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 14:32:59 -0400 Subject: [PATCH 067/368] Fix some rebase errors --- Directory.Packages.props | 2 +- Directory.Packages.props.orig | 42 ++++++++----------- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 10 ++--- src/Ryujinx.Graphics.Metal/Pipeline.cs | 11 +++-- .../RenderEncoderState.cs | 32 +++++++------- src/Ryujinx/Ryujinx.csproj | 2 + .../UI/Renderer/EmbeddedWindowMetal.cs | 4 +- 7 files changed, 54 insertions(+), 49 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 73659e1d7..070b31cd6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + diff --git a/Directory.Packages.props.orig b/Directory.Packages.props.orig index 8f22ea60c..14097e354 100644 --- a/Directory.Packages.props.orig +++ b/Directory.Packages.props.orig @@ -3,13 +3,13 @@ true - - - - - - - + + + + + + + @@ -17,18 +17,15 @@ - -<<<<<<< HEAD +<<<<<<< HEAD - ======= - - - ->>>>>>> 45a6dffcf (Bump SharpMetal) + +>>>>>>> 546c1ffc0 (Fix some rebase errors) + @@ -39,29 +36,26 @@ - + - + - + <<<<<<< HEAD +======= + +>>>>>>> 546c1ffc0 (Fix some rebase errors) -======= - - - - ->>>>>>> 45a6dffcf (Bump SharpMetal) - \ No newline at end of file + diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index 0e23d8804..d0987f0fe 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -1,6 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; -using SharpMetal; +using SharpMetal.Metal; namespace Ryujinx.Graphics.Metal { @@ -168,7 +168,7 @@ namespace Ryujinx.Graphics.Metal { return target switch { - Target.TextureBuffer => MTLTextureType.TypeTextureBuffer, + Target.TextureBuffer => MTLTextureType.TextureBuffer, Target.Texture1D => MTLTextureType.Type1D, Target.Texture1DArray => MTLTextureType.Type1DArray, Target.Texture2D => MTLTextureType.Type2D, @@ -176,8 +176,8 @@ namespace Ryujinx.Graphics.Metal Target.Texture2DMultisample => MTLTextureType.Type2DMultisample, Target.Texture2DMultisampleArray => MTLTextureType.Type2DMultisampleArray, Target.Texture3D => MTLTextureType.Type3D, - Target.Cubemap => MTLTextureType.TypeCube, - Target.CubemapArray => MTLTextureType.TypeCubeArray, + Target.Cubemap => MTLTextureType.Cube, + Target.CubemapArray => MTLTextureType.CubeArray, _ => LogInvalidAndReturn(target, nameof(Target), MTLTextureType.Type2D) }; } @@ -203,4 +203,4 @@ namespace Ryujinx.Graphics.Metal return defaultValue; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 16e678710..254f3e842 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -23,12 +23,12 @@ namespace Ryujinx.Graphics.Metal private readonly HelperShaders _helperShaders; private MTLCommandBuffer _commandBuffer; - private MTLCommandEncoder _currentEncoder; - private MTLTexture[] _renderTargets = Array.Empty(); + private MTLCommandEncoder? _currentEncoder; + private MTLTexture[] _renderTargets = []; private RenderEncoderState _renderEncoderState; private readonly MTLVertexDescriptor _vertexDescriptor = new(); - private MTLBuffer[] _vertexBuffers = Array.Empty(); + private MTLBuffer[] _vertexBuffers = []; private MTLBuffer _indexBuffer; private MTLIndexType _indexType; @@ -393,6 +393,11 @@ namespace Ryujinx.Graphics.Metal } } + public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) + { + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + } + public void SetImage(int binding, ITexture texture, Format imageFormat) { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 78013833c..d58fae206 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -11,15 +11,15 @@ namespace Ryujinx.Graphics.Metal struct RenderEncoderState { private readonly MTLDevice _device; - private readonly MTLFunction _vertexFunction = null; - private readonly MTLFunction _fragmentFunction = null; - private MTLDepthStencilState _depthStencilState = null; + private readonly MTLFunction? _vertexFunction = null; + private readonly MTLFunction? _fragmentFunction = null; + private MTLDepthStencilState? _depthStencilState = null; private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always; private bool _depthWriteEnabled = false; - private MTLStencilDescriptor _backFaceStencil = null; - private MTLStencilDescriptor _frontFaceStencil = null; + private MTLStencilDescriptor? _backFaceStencil = null; + private MTLStencilDescriptor? _frontFaceStencil = null; public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; @@ -41,12 +41,12 @@ namespace Ryujinx.Graphics.Metal if (_vertexFunction != null) { - renderPipelineDescriptor.VertexFunction = _vertexFunction; + renderPipelineDescriptor.VertexFunction = _vertexFunction.Value; } if (_fragmentFunction != null) { - renderPipelineDescriptor.FragmentFunction = _fragmentFunction; + renderPipelineDescriptor.FragmentFunction = _fragmentFunction.Value; } var attachment = renderPipelineDescriptor.ColorAttachments.Object(0); @@ -70,7 +70,7 @@ namespace Ryujinx.Graphics.Metal if (_depthStencilState != null) { - renderCommandEncoder.SetDepthStencilState(_depthStencilState); + renderCommandEncoder.SetDepthStencilState(_depthStencilState.Value); } } @@ -79,13 +79,15 @@ namespace Ryujinx.Graphics.Metal _backFaceStencil = backFace; _frontFaceStencil = frontFace; - return _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor { DepthCompareFunction = _depthCompareFunction, DepthWriteEnabled = _depthWriteEnabled, - BackFaceStencil = _backFaceStencil, - FrontFaceStencil = _frontFaceStencil + BackFaceStencil = _backFaceStencil.Value, + FrontFaceStencil = _frontFaceStencil.Value }); + + return _depthStencilState.Value; } public MTLDepthStencilState UpdateDepthState(MTLCompareFunction depthCompareFunction, bool depthWriteEnabled) @@ -93,13 +95,15 @@ namespace Ryujinx.Graphics.Metal _depthCompareFunction = depthCompareFunction; _depthWriteEnabled = depthWriteEnabled; - return _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor { DepthCompareFunction = _depthCompareFunction, DepthWriteEnabled = _depthWriteEnabled, - BackFaceStencil = _backFaceStencil, - FrontFaceStencil = _frontFaceStencil + BackFaceStencil = _backFaceStencil.Value, + FrontFaceStencil = _frontFaceStencil.Value }); + + return _depthStencilState.Value; } } } diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index ce75b1d87..8aac74a91 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -59,6 +59,7 @@ + @@ -66,6 +67,7 @@ + diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs index a8bac75c0..5373f8ed1 100644 --- a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs +++ b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs @@ -2,7 +2,7 @@ using SPB.Windowing; using SPB.Platform.Metal; using System; -namespace Ryujinx.UI.Renderer +namespace Ryujinx.Ava.UI.Renderer { public class EmbeddedWindowMetal : EmbeddedWindow { @@ -22,4 +22,4 @@ namespace Ryujinx.UI.Renderer return simpleMetalWindow; } } -} \ No newline at end of file +} -- 2.47.1 From 76bafe75f4e8c23b0f87f112a5fdae6fe7fe9b2c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 14:48:54 -0400 Subject: [PATCH 068/368] FIx build --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 83 ++++++++++++++----- src/Ryujinx/AppHost.cs | 2 +- .../UI/Renderer/EmbeddedWindowMetal.cs | 19 +---- src/Ryujinx/UI/Renderer/RendererHost.axaml.cs | 13 ++- 4 files changed, 73 insertions(+), 44 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 254f3e842..b1c3c03de 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -10,6 +10,13 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + enum EncoderType + { + Blit, + Compute, + Render + } + [SupportedOSPlatform("macos")] class Pipeline : IPipeline, IDisposable { @@ -24,6 +31,7 @@ namespace Ryujinx.Graphics.Metal private MTLCommandBuffer _commandBuffer; private MTLCommandEncoder? _currentEncoder; + private EncoderType _currentEncoderType; private MTLTexture[] _renderTargets = []; private RenderEncoderState _renderEncoderState; @@ -77,9 +85,12 @@ namespace Ryujinx.Graphics.Metal public MTLRenderCommandEncoder GetOrCreateRenderEncoder() { - if (_currentEncoder is MTLRenderCommandEncoder encoder) + if (_currentEncoder != null) { - return encoder; + if (_currentEncoderType == EncoderType.Render) + { + return new MTLRenderCommandEncoder(_currentEncoder.Value); + } } return BeginRenderPass(); @@ -87,9 +98,12 @@ namespace Ryujinx.Graphics.Metal public MTLBlitCommandEncoder GetOrCreateBlitEncoder() { - if (_currentEncoder is MTLBlitCommandEncoder encoder) + if (_currentEncoder != null) { - return encoder; + if (_currentEncoderType == EncoderType.Blit) + { + return new MTLBlitCommandEncoder(_currentEncoder.Value); + } } return BeginBlitPass(); @@ -97,9 +111,12 @@ namespace Ryujinx.Graphics.Metal public MTLComputeCommandEncoder GetOrCreateComputeEncoder() { - if (_currentEncoder is MTLComputeCommandEncoder encoder) + if (_currentEncoder != null) { - return encoder; + if (_currentEncoderType == EncoderType.Compute) + { + return new MTLComputeCommandEncoder(_currentEncoder.Value); + } } return BeginComputePass(); @@ -109,8 +126,23 @@ namespace Ryujinx.Graphics.Metal { if (_currentEncoder != null) { - _currentEncoder.EndEncoding(); - _currentEncoder = null; + switch (_currentEncoderType) + { + case EncoderType.Blit: + new MTLBlitCommandEncoder(_currentEncoder.Value).EndEncoding(); + _currentEncoder = null; + break; + case EncoderType.Compute: + new MTLComputeCommandEncoder(_currentEncoder.Value).EndEncoding(); + _currentEncoder = null; + break; + case EncoderType.Render: + new MTLRenderCommandEncoder(_currentEncoder.Value).EndEncoding(); + _currentEncoder = null; + break; + default: + throw new ArgumentOutOfRangeException(); + } } } @@ -140,6 +172,7 @@ namespace Ryujinx.Graphics.Metal } _currentEncoder = renderCommandEncoder; + _currentEncoderType = EncoderType.Render; return renderCommandEncoder; } @@ -151,6 +184,7 @@ namespace Ryujinx.Graphics.Metal var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); _currentEncoder = blitCommandEncoder; + _currentEncoderType = EncoderType.Blit; return blitCommandEncoder; } @@ -162,6 +196,7 @@ namespace Ryujinx.Graphics.Metal var computeCommandEncoder = _commandBuffer.ComputeCommandEncoder(descriptor); _currentEncoder = computeCommandEncoder; + _currentEncoderType = EncoderType.Compute; return computeCommandEncoder; } @@ -175,9 +210,13 @@ namespace Ryujinx.Graphics.Metal EndCurrentPass(); var descriptor = new MTLRenderPassDescriptor(); - descriptor.ColorAttachments.Object(0).Texture = drawable.Texture; - descriptor.ColorAttachments.Object(0).LoadAction = MTLLoadAction.Clear; - descriptor.ColorAttachments.Object(0).ClearColor = _clearColor; + var colorAttachment = descriptor.ColorAttachments.Object(0); + + colorAttachment.Texture = drawable.Texture; + colorAttachment.LoadAction = MTLLoadAction.Clear; + colorAttachment.ClearColor = _clearColor; + + descriptor.ColorAttachments.SetObject(colorAttachment, 0); var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); @@ -352,9 +391,9 @@ namespace Ryujinx.Graphics.Metal depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always, depthTest.WriteEnable); - if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + if (_currentEncoderType == EncoderType.Render) { - renderCommandEncoder.SetDepthStencilState(depthStencilState); + new MTLRenderCommandEncoder(_currentEncoder.Value).SetDepthStencilState(depthStencilState); } } @@ -362,9 +401,9 @@ namespace Ryujinx.Graphics.Metal { var cullMode = enable ? face.Convert() : MTLCullMode.None; - if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + if (_currentEncoderType == EncoderType.Render) { - renderCommandEncoder.SetCullMode(cullMode); + new MTLRenderCommandEncoder(_currentEncoder.Value).SetCullMode(cullMode); } _renderEncoderState.CullMode = cullMode; @@ -374,9 +413,9 @@ namespace Ryujinx.Graphics.Metal { var winding = frontFace.Convert(); - if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + if (_currentEncoderType == EncoderType.Render) { - renderCommandEncoder.SetFrontFacingWinding(winding); + new MTLRenderCommandEncoder(_currentEncoder.Value).SetFrontFacingWinding(winding); } _renderEncoderState.Winding = winding; @@ -545,9 +584,9 @@ namespace Ryujinx.Graphics.Metal var depthStencilState = _renderEncoderState.UpdateStencilState(backFace, frontFace); - if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + if (_currentEncoderType == EncoderType.Render) { - renderCommandEncoder.SetDepthStencilState(depthStencilState); + new MTLRenderCommandEncoder(_currentEncoder.Value).SetDepthStencilState(depthStencilState); } } @@ -594,7 +633,11 @@ namespace Ryujinx.Graphics.Metal { if (vertexBuffers[i].Stride != 0) { - _vertexDescriptor.Layouts.Object((ulong)i).Stride = (ulong)vertexBuffers[i].Stride; + var layout = _vertexDescriptor.Layouts.Object(0); + + layout.Stride = (ulong)vertexBuffers[i].Stride; + + _vertexDescriptor.Layouts.SetObject(layout, (ulong)i); _vertexBuffers[i] = _device.NewBuffer( vertexBuffers[i].Buffer.Handle.ToIntPtr(), (ulong)vertexBuffers[i].Buffer.Size, diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 31a81193a..2e46cca0d 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -905,7 +905,7 @@ namespace Ryujinx.Ava } else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal && OperatingSystem.IsMacOS()) { - renderer = new MetalRenderer(); + renderer = new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal).CreateSurface); } else { diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs index 5373f8ed1..e55b75715 100644 --- a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs +++ b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs @@ -1,25 +1,12 @@ -using SPB.Windowing; -using SPB.Platform.Metal; -using System; +using SharpMetal.QuartzCore; namespace Ryujinx.Ava.UI.Renderer { public class EmbeddedWindowMetal : EmbeddedWindow { - public SimpleMetalWindow CreateSurface() + public CAMetalLayer CreateSurface() { - SimpleMetalWindow simpleMetalWindow; - - if (OperatingSystem.IsMacOS()) - { - simpleMetalWindow = new SimpleMetalWindow(new NativeHandle(NsView), new NativeHandle(MetalLayer)); - } - else - { - throw new PlatformNotSupportedException(); - } - - return simpleMetalWindow; + return new CAMetalLayer(MetalLayer); } } } diff --git a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs index 4bf10d0d7..7009a9377 100644 --- a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs +++ b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs @@ -17,14 +17,13 @@ namespace Ryujinx.Ava.UI.Renderer { InitializeComponent(); - if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl) + EmbeddedWindow = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch { - EmbeddedWindow = new EmbeddedWindowOpenGL(); - } - else - { - EmbeddedWindow = new EmbeddedWindowVulkan(); - } + GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(), + GraphicsBackend.Metal => new EmbeddedWindowMetal(), + GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(), + _ => throw new NotSupportedException() + }; Initialize(); } -- 2.47.1 From c0da3d68cade321b406d98529585b1f2d2b25f5b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 14:51:44 -0400 Subject: [PATCH 069/368] Formatting --- src/Ryujinx.Graphics.Metal/Constants.cs | 2 +- src/Ryujinx.Graphics.Metal/HardwareInfo.cs | 2 +- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs | 2 +- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 2 +- src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 2 +- src/Ryujinx/AppHost.cs | 2 +- src/Ryujinx/Ryujinx.csproj | 1 - 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 06fd84a52..21776ee58 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -10,4 +10,4 @@ namespace Ryujinx.Graphics.Metal public const int MaxCommandBuffersPerQueue = 16; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs b/src/Ryujinx.Graphics.Metal/HardwareInfo.cs index 13566dbd8..4b3b710f8 100644 --- a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs +++ b/src/Ryujinx.Graphics.Metal/HardwareInfo.cs @@ -79,4 +79,4 @@ namespace Ryujinx.Graphics.Metal return ""; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index d771f9a22..7ba9647ac 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -260,4 +260,4 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index cf1ad906f..f67e1cb3f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -102,4 +102,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return indentation; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 956483a45..98e681f3a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -198,4 +198,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 94801b96b..70f4d5080 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -220,4 +220,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return ReinterpretCast(context, cond, srcType, AggregateType.Bool); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 2e46cca0d..110b73e7e 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -28,9 +28,9 @@ using Ryujinx.Common.Utilities; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; +using Ryujinx.Graphics.Metal; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; -using Ryujinx.Graphics.Metal; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 8aac74a91..d5bad2ee6 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -59,7 +59,6 @@ - -- 2.47.1 From b8630b5c45299d2e5238dfbf566013d24f317557 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 14:57:23 -0400 Subject: [PATCH 070/368] Resolve warning --- src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs index e55b75715..c832c2211 100644 --- a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs +++ b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs @@ -1,7 +1,9 @@ using SharpMetal.QuartzCore; +using System.Runtime.Versioning; namespace Ryujinx.Ava.UI.Renderer { + [SupportedOSPlatform("macos")] public class EmbeddedWindowMetal : EmbeddedWindow { public CAMetalLayer CreateSurface() -- 2.47.1 From 6685041545a61dd2ff9077115c27d32e9cc34dbf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 15:03:10 -0400 Subject: [PATCH 071/368] Try again --- src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs index c832c2211..eaf6f7bdf 100644 --- a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs +++ b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs @@ -1,14 +1,20 @@ using SharpMetal.QuartzCore; -using System.Runtime.Versioning; +using System; namespace Ryujinx.Ava.UI.Renderer { - [SupportedOSPlatform("macos")] public class EmbeddedWindowMetal : EmbeddedWindow { public CAMetalLayer CreateSurface() { - return new CAMetalLayer(MetalLayer); + if (OperatingSystem.IsMacOS()) + { + return new CAMetalLayer(MetalLayer); + } + else + { + throw new NotSupportedException(); + } } } } -- 2.47.1 From b157a8e5498171faa6c04ea107026dddf93b60cc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 14:05:09 -0400 Subject: [PATCH 072/368] Revise ISampler --- src/Ryujinx.Graphics.Metal/CounterEvent.cs | 1 - src/Ryujinx.Graphics.Metal/HelperShaders.cs | 4 +-- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 20 +---------- src/Ryujinx.Graphics.Metal/Pipeline.cs | 37 +++++++++++++++------ src/Ryujinx.Graphics.Metal/Sampler.cs | 31 +++++++++++++++-- src/Ryujinx.Graphics.Metal/Texture.cs | 8 ++++- 6 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/CounterEvent.cs b/src/Ryujinx.Graphics.Metal/CounterEvent.cs index 1773b9b63..46b04997e 100644 --- a/src/Ryujinx.Graphics.Metal/CounterEvent.cs +++ b/src/Ryujinx.Graphics.Metal/CounterEvent.cs @@ -4,7 +4,6 @@ namespace Ryujinx.Graphics.Metal { class CounterEvent : ICounterEvent { - public CounterEvent() { Invalid = false; diff --git a/src/Ryujinx.Graphics.Metal/HelperShaders.cs b/src/Ryujinx.Graphics.Metal/HelperShaders.cs index 7c1eada7b..8ca7adb6f 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShaders.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShaders.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Metal Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Library: {StringHelper.String(error.LocalizedDescription)}"); } - BlitShader = new HelperShader(device, library, "vertexBlit", "fragmentBlit"); + BlitShader = new HelperShader(library, "vertexBlit", "fragmentBlit"); } } @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Metal public readonly MTLFunction VertexFunction; public readonly MTLFunction FragmentFunction; - public HelperShader(MTLDevice device, MTLLibrary library, string vertex, string fragment) + public HelperShader(MTLLibrary library, string vertex, string fragment) { VertexFunction = library.NewFunction(StringHelper.NSString(vertex)); FragmentFunction = library.NewFunction(StringHelper.NSString(fragment)); diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index a58b7cb60..0930be9ef 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -84,25 +84,7 @@ namespace Ryujinx.Graphics.Metal public ISampler CreateSampler(SamplerCreateInfo info) { - (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); - - var sampler = _device.NewSamplerState(new MTLSamplerDescriptor - { - BorderColor = MTLSamplerBorderColor.TransparentBlack, - MinFilter = minFilter, - MagFilter = info.MagFilter.Convert(), - MipFilter = mipFilter, - CompareFunction = info.CompareOp.Convert(), - LodMinClamp = info.MinLod, - LodMaxClamp = info.MaxLod, - LodAverage = false, - MaxAnisotropy = (uint)info.MaxAnisotropy, - SAddressMode = info.AddressU.Convert(), - TAddressMode = info.AddressV.Convert(), - RAddressMode = info.AddressP.Convert() - }); - - return new Sampler(sampler); + return new Sampler(_device, info); } public ITexture CreateTexture(TextureCreateInfo info) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index b1c3c03de..4d182b7ef 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -323,7 +323,6 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = GetOrCreateRenderEncoder(); - // TODO: Support topology re-indexing to provide support for TriangleFans var primitiveType = _renderEncoderState.Topology.Convert(); @@ -332,26 +331,36 @@ namespace Ryujinx.Graphics.Metal public void DrawIndexedIndirect(BufferRange indirectBuffer) { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndirect(BufferRange indirectBuffer) { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } @@ -437,15 +446,10 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public void SetImage(int binding, ITexture texture, Format imageFormat) - { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - } - public void SetLineParameters(float width, bool smooth) { // Not supported in Metal - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + Logger.Warning?.Print(LogClass.Gpu, "Wide-line is not supported without private Metal API"); } public void SetLogicOpState(bool enable, LogicalOp op) @@ -493,7 +497,7 @@ namespace Ryujinx.Graphics.Metal { Program prg = (Program)program; - if (prg.VertexFunction == null) + if (prg.VertexFunction == IntPtr.Zero) { Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!"); return; @@ -556,7 +560,10 @@ namespace Ryujinx.Graphics.Metal fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) { // TODO: Fix this function which currently wont accept pointer as intended - // _renderCommandEncoder.SetScissorRects(pMtlScissorRects, regions.Length); + if (_currentEncoderType == EncoderType.Render) + { + // new MTLRenderCommandEncoder(_currentEncoder.Value).SetScissorRects(pMtlScissorRects, (ulong)regions.Length); + } } } @@ -621,6 +628,7 @@ namespace Ryujinx.Graphics.Metal attrib.Format = MTLVertexFormat.Float4; attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; attrib.Offset = (ulong)vertexAttribs[i].Offset; + _vertexDescriptor.Attributes.SetObject(attrib, (ulong)i); } } } @@ -668,17 +676,26 @@ namespace Ryujinx.Graphics.Metal fixed (MTLViewport* pMtlViewports = mtlViewports) { // TODO: Fix this function which currently wont accept pointer as intended - // _renderCommandEncoder.SetViewports(pMtlViewports, viewports.Length); + if (_currentEncoderType == EncoderType.Render) + { + // new MTLRenderCommandEncoder(_currentEncoder.Value).SetViewports(pMtlViewports, (ulong)regions.Length); + } } } public void TextureBarrier() { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + + // renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, ); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void TextureBarrierTiled() { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + + // renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, ); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index cc1923cc3..f4ffecc02 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -1,15 +1,40 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + [SupportedOSPlatform("macos")] class Sampler : ISampler { - // private readonly MTLSamplerState _mtlSamplerState; + private readonly MTLSamplerState _mtlSamplerState; - public Sampler(MTLSamplerState mtlSamplerState) + public Sampler(MTLDevice device, SamplerCreateInfo info) { - // _mtlSamplerState = mtlSamplerState; + (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); + + var samplerState = device.NewSamplerState(new MTLSamplerDescriptor + { + BorderColor = MTLSamplerBorderColor.TransparentBlack, + MinFilter = minFilter, + MagFilter = info.MagFilter.Convert(), + MipFilter = mipFilter, + CompareFunction = info.CompareOp.Convert(), + LodMinClamp = info.MinLod, + LodMaxClamp = info.MaxLod, + LodAverage = false, + MaxAnisotropy = (uint)info.MaxAnisotropy, + SAddressMode = info.AddressU.Convert(), + TAddressMode = info.AddressV.Convert(), + RAddressMode = info.AddressP.Convert() + }); + + _mtlSamplerState = samplerState; + } + + public MTLSamplerState GetSampler() + { + return _mtlSamplerState; } public void Dispose() diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 7ba9647ac..43ead5bcf 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -88,6 +88,12 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + + if (destination is Texture destinationTexture) + { + + } Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } @@ -120,7 +126,7 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - return this; + throw new NotImplementedException(); } public PinnedSpan GetData() -- 2.47.1 From 94e15aa66262000ade1f48291e34b7bdaec7c4a5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 15:07:35 -0400 Subject: [PATCH 073/368] Bind Textures & Samplers --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 34 +++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 4d182b7ef..a2d427163 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -604,7 +604,39 @@ namespace Ryujinx.Graphics.Metal public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (texture is Texture tex) + { + if (sampler is Sampler samp) + { + MTLRenderCommandEncoder renderCommandEncoder; + MTLComputeCommandEncoder computeCommandEncoder; + + var mtlTexture = tex.MTLTexture; + var mtlSampler = samp.GetSampler(); + var index = (ulong)binding; + + switch (stage) + { + case ShaderStage.Fragment: + renderCommandEncoder = GetOrCreateRenderEncoder(); + renderCommandEncoder.SetFragmentTexture(mtlTexture, index); + renderCommandEncoder.SetFragmentSamplerState(mtlSampler, index); + break; + case ShaderStage.Vertex: + renderCommandEncoder = GetOrCreateRenderEncoder(); + renderCommandEncoder.SetVertexTexture(mtlTexture, index); + renderCommandEncoder.SetVertexSamplerState(mtlSampler, index); + break; + case ShaderStage.Compute: + computeCommandEncoder = GetOrCreateComputeEncoder(); + computeCommandEncoder.SetTexture(mtlTexture, index); + computeCommandEncoder.SetSamplerState(mtlSampler, index); + break; + default: + throw new ArgumentOutOfRangeException(nameof(stage), stage, "Unsupported shader stage!"); + } + } + } } public void SetUniformBuffers(ReadOnlySpan buffers) -- 2.47.1 From de7b3e7daccbb7f51d93cd32e36760da5cae90eb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 15:07:50 -0400 Subject: [PATCH 074/368] Remove capture code --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 43 -------------------------- 1 file changed, 43 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index a2d427163..91ae98c6d 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -20,11 +20,6 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Pipeline : IPipeline, IDisposable { - // 0 Frames = No capture - // Some games like Undertale trigger a stack overflow on capture end - private const int MaxFramesPerCapture = 0; - private const string CaptureLocation = "/Users/isaacmarovitz/Desktop/Captures/Trace-"; - private readonly MTLDevice _device; private readonly MTLCommandQueue _commandQueue; private readonly HelperShaders _helperShaders; @@ -42,8 +37,6 @@ namespace Ryujinx.Graphics.Metal private MTLIndexType _indexType; private ulong _indexBufferOffset; private MTLClearColor _clearColor; - private int _frameCount; - private bool _captureEnded = true; public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { @@ -57,30 +50,6 @@ namespace Ryujinx.Graphics.Metal _device); _commandBuffer = _commandQueue.CommandBuffer(); - - if (MaxFramesPerCapture > 0) - { - StartCapture(); - } - } - - private void StartCapture() - { - var captureDescriptor = new MTLCaptureDescriptor - { - CaptureObject = _commandQueue, - Destination = MTLCaptureDestination.GPUTraceDocument, - OutputURL = NSURL.FileURLWithPath(StringHelper.NSString(CaptureLocation + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".gputrace")) - }; - var captureError = new NSError(IntPtr.Zero); - MTLCaptureManager.SharedCaptureManager().StartCapture(captureDescriptor, ref captureError); - if (captureError != IntPtr.Zero) - { - Console.WriteLine($"Failed to start capture! {StringHelper.String(captureError.LocalizedDescription)}"); - - } - - _captureEnded = false; } public MTLRenderCommandEncoder GetOrCreateRenderEncoder() @@ -237,18 +206,6 @@ namespace Ryujinx.Graphics.Metal _commandBuffer.PresentDrawable(drawable); _commandBuffer.Commit(); - if (!_captureEnded) - { - _frameCount++; - - if (_frameCount >= MaxFramesPerCapture) - { - _captureEnded = true; - MTLCaptureManager.SharedCaptureManager().StopCapture(); - Logger.Warning?.Print(LogClass.Gpu, "Trace ended!"); - } - } - _commandBuffer = _commandQueue.CommandBuffer(); } -- 2.47.1 From 89d1caf30fcfd682ad44d986df9b49d2aa5e5acb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 15:08:39 -0400 Subject: [PATCH 075/368] Dont set Vertex Attributes for now --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 91ae98c6d..063f33fbe 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -613,11 +613,11 @@ namespace Ryujinx.Graphics.Metal if (!vertexAttribs[i].IsZero) { // TODO: Format should not be hardcoded - var attrib = _vertexDescriptor.Attributes.Object((ulong)i); - attrib.Format = MTLVertexFormat.Float4; - attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; - attrib.Offset = (ulong)vertexAttribs[i].Offset; - _vertexDescriptor.Attributes.SetObject(attrib, (ulong)i); + // var attrib = _vertexDescriptor.Attributes.Object((ulong)i); + // attrib.Format = MTLVertexFormat.Float4; + // attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; + // attrib.Offset = (ulong)vertexAttribs[i].Offset; + // _vertexDescriptor.Attributes.SetObject(attrib, (ulong)i); } } } -- 2.47.1 From 7a99143a8ab050cb0c09e4267146725a888a5002 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 16:14:37 -0400 Subject: [PATCH 076/368] Fix MSL Reinterpret Casts --- .../CodeGen/Msl/TypeConversion.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs index 44666c323..cb1075d08 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; +using System; namespace Ryujinx.Graphics.Shader.CodeGen.Msl { @@ -39,11 +40,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl switch (dstType) { case AggregateType.Bool: - return $"(floatBitsToInt({expr}) != 0)"; + return $"(as_type({expr}) != 0)"; case AggregateType.S32: - return $"floatBitsToInt({expr})"; + return $"as_type({expr})"; case AggregateType.U32: - return $"floatBitsToUint({expr})"; + return $"as_type({expr})"; } } else if (dstType == AggregateType.FP32) @@ -51,11 +52,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl switch (srcType) { case AggregateType.Bool: - return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, AggregateType.S32)})"; + return $"as_type({ReinterpretBoolToInt(expr, node, AggregateType.S32)})"; case AggregateType.S32: - return $"intBitsToFloat({expr})"; + return $"as_type({expr})"; case AggregateType.U32: - return $"uintBitsToFloat({expr})"; + return $"as_type({expr})"; } } else if (srcType == AggregateType.Bool) -- 2.47.1 From b7a0aefa80220822d65885c20554af2e36aac1d4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 16:16:00 -0400 Subject: [PATCH 077/368] Make TypeConversion failure an error --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs index cb1075d08..55a4eb537 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs @@ -78,9 +78,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return $"uint({expr})"; } - Logger.Warning?.Print(LogClass.Gpu, $"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\"."); - // TODO: Make this an error again - return $"INVALID CAST ({expr})"; + throw new ArgumentException($"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\"."); } private static string ReinterpretBoolToInt(string expr, IAstNode node, AggregateType dstType) -- 2.47.1 From dc4305f1cf55fabd9d8e921e970857c2865fa641 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 16:23:43 -0400 Subject: [PATCH 078/368] Formatting --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 14 +++++++------- src/Ryujinx.Graphics.Metal/Texture.cs | 12 ++++++------ .../CodeGen/Msl/TypeConversion.cs | 1 - 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 063f33fbe..d1731f6d9 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -288,35 +288,35 @@ namespace Ryujinx.Graphics.Metal public void DrawIndexedIndirect(BufferRange indirectBuffer) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndirect(BufferRange indirectBuffer) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } @@ -674,7 +674,7 @@ namespace Ryujinx.Graphics.Metal public void TextureBarrier() { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(); // renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, ); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); @@ -682,7 +682,7 @@ namespace Ryujinx.Graphics.Metal public void TextureBarrierTiled() { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(); // renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, ); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 43ead5bcf..bb6959b14 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -88,12 +88,12 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); - - if (destination is Texture destinationTexture) - { - - } + // var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + // + // if (destination is Texture destinationTexture) + // { + // + // } Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs index 55a4eb537..e145bb8b0 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Logging; using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; -- 2.47.1 From e2445990a5658de90fd6c9b7febb4310023c2819 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 17:18:59 -0400 Subject: [PATCH 079/368] =?UTF-8?q?Fix=20some=20shader=20gen=20problems?= =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CodeGen/Msl/Declarations.cs | 28 +++++++++++-------- .../CodeGen/Msl/Instructions/IoMap.cs | 2 +- .../CodeGen/Msl/MslGenerator.cs | 6 ++-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 98e681f3a..4580df203 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -65,11 +65,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { if (stage == ShaderStage.Vertex) { - context.AppendLine("VertexOutput out;"); + context.AppendLine("VertexOut out;"); } else if (stage == ShaderStage.Fragment) { - context.AppendLine("FragmentOutput out;"); + context.AppendLine("FragmentOut out;"); } foreach (AstOperand decl in function.Locals) @@ -120,17 +120,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl switch (context.Definitions.Stage) { case ShaderStage.Vertex: - prefix = "Vertex"; + context.AppendLine($"struct VertexIn"); break; case ShaderStage.Fragment: - prefix = "Fragment"; + context.AppendLine($"struct VertexOut"); break; case ShaderStage.Compute: - prefix = "Compute"; + context.AppendLine($"struct ComputeIn"); break; } - context.AppendLine($"struct {prefix}In"); context.EnterScope(); foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) @@ -162,31 +161,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl switch (context.Definitions.Stage) { case ShaderStage.Vertex: - prefix = "Vertex"; + context.AppendLine($"struct VertexOut"); break; case ShaderStage.Fragment: - prefix = "Fragment"; + context.AppendLine($"struct FragmentOut"); break; case ShaderStage.Compute: - prefix = "Compute"; + context.AppendLine($"struct ComputeOut"); break; } - context.AppendLine($"struct {prefix}Output"); context.EnterScope(); foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { - string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)); + string type = ioDefinition.IoVariable switch + { + IoVariable.Position => "float4", + IoVariable.PointSize => "float", + _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) + }; string name = ioDefinition.IoVariable switch { IoVariable.Position => "position", + IoVariable.PointSize => "point_size", IoVariable.FragmentOutputColor => "color", _ => $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch { IoVariable.Position => " [[position]]", + IoVariable.PointSize => " [[point_size]]", + IoVariable.FragmentOutputColor => $" [[color({ioDefinition.Location})]]", _ => "" }; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 5c7800de8..2ec7a1779 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.FrontFacing => ("front_facing", AggregateType.Bool), IoVariable.InstanceId => ("instance_id", AggregateType.S32), IoVariable.PointCoord => ("point_coord", AggregateType.Vector2), - IoVariable.PointSize => ("point_size", AggregateType.FP32), + IoVariable.PointSize => ("out.point_size", AggregateType.FP32), IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 70f4d5080..0e56629fe 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -85,13 +85,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { funcKeyword = "vertex"; funcName = "vertexMain"; - returnType = "VertexOutput"; + returnType = "VertexOut"; } else if (stage == ShaderStage.Fragment) { funcKeyword = "fragment"; funcName = "fragmentMain"; - returnType = "FragmentOutput"; + returnType = "FragmentOut"; } else if (stage == ShaderStage.Compute) { @@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else if (stage == ShaderStage.Fragment) { - args = args.Prepend("FragmentIn in").ToArray(); + args = args.Prepend("VertexOut in").ToArray(); } else if (stage == ShaderStage.Compute) { -- 2.47.1 From fc7f09624c9caeb27322d73896b257c59d7da324 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 19:02:05 -0400 Subject: [PATCH 080/368] Fix VertexBuffers Naive non-managed approach --- src/Ryujinx.Graphics.Metal/BufferInfo.cs | 10 +++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 33 +++++++++++------------- 2 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/BufferInfo.cs diff --git a/src/Ryujinx.Graphics.Metal/BufferInfo.cs b/src/Ryujinx.Graphics.Metal/BufferInfo.cs new file mode 100644 index 000000000..72deca3d8 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/BufferInfo.cs @@ -0,0 +1,10 @@ +using System; + +namespace Ryujinx.Graphics.Metal +{ + public struct BufferInfo + { + public IntPtr Handle; + public int Offset; + } +} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index d1731f6d9..084af94e9 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Metal private RenderEncoderState _renderEncoderState; private readonly MTLVertexDescriptor _vertexDescriptor = new(); - private MTLBuffer[] _vertexBuffers = []; + private BufferInfo[] _vertexBuffers = []; private MTLBuffer _indexBuffer; private MTLIndexType _indexType; @@ -134,10 +134,7 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < _vertexBuffers.Length; i++) { - if (_vertexBuffers[i] != null) - { - renderCommandEncoder.SetVertexBuffer(_vertexBuffers[i], 0, (ulong)i); - } + renderCommandEncoder.SetVertexBuffer(new MTLBuffer(_vertexBuffers[i].Handle), (ulong)_vertexBuffers[i].Offset, (ulong)i); } _currentEncoder = renderCommandEncoder; @@ -613,32 +610,32 @@ namespace Ryujinx.Graphics.Metal if (!vertexAttribs[i].IsZero) { // TODO: Format should not be hardcoded - // var attrib = _vertexDescriptor.Attributes.Object((ulong)i); - // attrib.Format = MTLVertexFormat.Float4; - // attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; - // attrib.Offset = (ulong)vertexAttribs[i].Offset; - // _vertexDescriptor.Attributes.SetObject(attrib, (ulong)i); + var attrib = _vertexDescriptor.Attributes.Object((ulong)i); + attrib.Format = MTLVertexFormat.Float4; + attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; + attrib.Offset = (ulong)vertexAttribs[i].Offset; + + var layout = _vertexDescriptor.Layouts.Object((ulong)vertexAttribs[i].BufferIndex); + layout.Stride = 1; } } } public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - _vertexBuffers = new MTLBuffer[vertexBuffers.Length]; + _vertexBuffers = new BufferInfo[vertexBuffers.Length]; for (int i = 0; i < vertexBuffers.Length; i++) { if (vertexBuffers[i].Stride != 0) { - var layout = _vertexDescriptor.Layouts.Object(0); - + var layout = _vertexDescriptor.Layouts.Object((ulong)i); layout.Stride = (ulong)vertexBuffers[i].Stride; - _vertexDescriptor.Layouts.SetObject(layout, (ulong)i); - _vertexBuffers[i] = _device.NewBuffer( - vertexBuffers[i].Buffer.Handle.ToIntPtr(), - (ulong)vertexBuffers[i].Buffer.Size, - MTLResourceOptions.ResourceStorageModeManaged); + _vertexBuffers[i] = new BufferInfo { + Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(), + Offset = vertexBuffers[i].Buffer.Offset + }; } } } -- 2.47.1 From 987a42ce30f483dc2cbe4dc8805539ed2808e23f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:04:31 -0400 Subject: [PATCH 081/368] Fix fragment shader bindings --- .../CodeGen/Msl/Declarations.cs | 10 ++++++++-- .../CodeGen/Msl/MslGenerator.cs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 4580df203..f4b6a7f5a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine($"struct VertexIn"); break; case ShaderStage.Fragment: - context.AppendLine($"struct VertexOut"); + context.AppendLine($"struct FragmentIn"); break; case ShaderStage.Compute: context.AppendLine($"struct ComputeIn"); @@ -136,7 +136,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)); string name = $"{DefaultNames.IAttributePrefix}{ioDefinition.Location}"; - string suffix = context.Definitions.Stage == ShaderStage.Vertex ? $" [[attribute({ioDefinition.Location})]]" : ""; + string suffix = context.Definitions.Stage switch + { + ShaderStage.Vertex => $" [[attribute({ioDefinition.Location})]]", + ShaderStage.Fragment => $" [[user(loc{ioDefinition.Location})]]", + _ => "" + }; context.AppendLine($"{type} {name}{suffix};"); } @@ -192,6 +197,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { IoVariable.Position => " [[position]]", IoVariable.PointSize => " [[point_size]]", + IoVariable.UserDefined => $" [[user(loc{ioDefinition.Location})]]", IoVariable.FragmentOutputColor => $" [[color({ioDefinition.Location})]]", _ => "" }; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 0e56629fe..ab591e5af 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else if (stage == ShaderStage.Fragment) { - args = args.Prepend("VertexOut in").ToArray(); + args = args.Prepend("VertexOut in [[stage_in]]").ToArray(); } else if (stage == ShaderStage.Compute) { -- 2.47.1 From 3117aeca7fb6240c0eb0f004a828aeea2b40f97b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:11:48 -0400 Subject: [PATCH 082/368] Cleanup Shader I/O --- .../CodeGen/Msl/Declarations.cs | 17 +++++++++-------- .../CodeGen/Msl/MslGenerator.cs | 8 +++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index f4b6a7f5a..2e87da675 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -63,13 +63,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public static void DeclareLocals(CodeGenContext context, StructuredFunction function, ShaderStage stage) { - if (stage == ShaderStage.Vertex) + switch (stage) { - context.AppendLine("VertexOut out;"); - } - else if (stage == ShaderStage.Fragment) - { - context.AppendLine("FragmentOut out;"); + case ShaderStage.Vertex: + context.AppendLine("VertexOut out;"); + break; + case ShaderStage.Fragment: + context.AppendLine("FragmentOut out;"); + break; } foreach (AstOperand decl in function.Locals) @@ -126,7 +127,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine($"struct FragmentIn"); break; case ShaderStage.Compute: - context.AppendLine($"struct ComputeIn"); + context.AppendLine($"struct KernelIn"); break; } @@ -172,7 +173,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine($"struct FragmentOut"); break; case ShaderStage.Compute: - context.AppendLine($"struct ComputeOut"); + context.AppendLine($"struct KernelOut"); break; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index ab591e5af..5e6f344fc 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -95,7 +95,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else if (stage == ShaderStage.Compute) { - // TODO: Compute main + funcKeyword = "kernel"; + funcName = "kernelMain"; + returnType = "void"; } if (context.AttributeUsage.UsedInputAttributes != 0) @@ -106,11 +108,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else if (stage == ShaderStage.Fragment) { - args = args.Prepend("VertexOut in [[stage_in]]").ToArray(); + args = args.Prepend("FragmentIn in [[stage_in]]").ToArray(); } else if (stage == ShaderStage.Compute) { - // TODO: Compute input + args = args.Prepend("KernelIn in [[stage_in]]").ToArray(); } } } -- 2.47.1 From b4f468c653905ceb28c1f4447187631a88c7cd12 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:15:31 -0400 Subject: [PATCH 083/368] Load attachments --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 084af94e9..9af2be908 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -126,6 +126,7 @@ namespace Ryujinx.Graphics.Metal { var attachment = descriptor.ColorAttachments.Object((ulong)i); attachment.Texture = _renderTargets[i]; + attachment.LoadAction = MTLLoadAction.Load; } } -- 2.47.1 From 44f4d41cf8bab5ddd485a0a2eb3d3723d7cde10d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:29:14 -0400 Subject: [PATCH 084/368] Blit at the end of the render --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 9af2be908..f52b473f8 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -186,6 +186,10 @@ namespace Ryujinx.Graphics.Metal descriptor.ColorAttachments.SetObject(colorAttachment, 0); var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); + _renderEncoderState = new RenderEncoderState( + _helperShaders.BlitShader.VertexFunction, + _helperShaders.BlitShader.FragmentFunction, + _device); _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); var sampler = _device.NewSamplerState(new MTLSamplerDescriptor -- 2.47.1 From 48aba086e1c6907826e35d49b574fc444a54f28a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:56:54 -0400 Subject: [PATCH 085/368] Fix swizzle for certain formats --- src/Ryujinx.Graphics.Metal/Texture.cs | 30 +++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index bb6959b14..01513470f 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -39,12 +39,34 @@ namespace Ryujinx.Graphics.Metal descriptor.SampleCount = (ulong)Info.Samples; descriptor.MipmapLevelCount = (ulong)Info.Levels; descriptor.TextureType = Info.Target.Convert(); + + var swizzleR = Info.SwizzleR.Convert(); + var swizzleG = Info.SwizzleG.Convert(); + var swizzleB = Info.SwizzleB.Convert(); + var swizzleA = Info.SwizzleA.Convert(); + + if (info.Format == Format.R5G5B5A1Unorm || + info.Format == Format.R5G5B5X1Unorm || + info.Format == Format.R5G6B5Unorm) + { + (swizzleB, swizzleR) = (swizzleR, swizzleB); + } else if (descriptor.PixelFormat == MTLPixelFormat.ABGR4Unorm || info.Format == Format.A1B5G5R5Unorm) + { + var tempB = swizzleB; + var tempA = swizzleA; + + swizzleB = swizzleG; + swizzleA = swizzleR; + swizzleR = tempA; + swizzleG = tempB; + } + descriptor.Swizzle = new MTLTextureSwizzleChannels { - red = Info.SwizzleR.Convert(), - green = Info.SwizzleG.Convert(), - blue = Info.SwizzleB.Convert(), - alpha = Info.SwizzleA.Convert() + red = swizzleR, + green = swizzleG, + blue = swizzleB, + alpha = swizzleA }; MTLTexture = _device.NewTexture(descriptor); -- 2.47.1 From b95e1d288bc71ba719ad6e09ce0ebaac6b532d29 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 22:14:17 -0400 Subject: [PATCH 086/368] Properly check for 3D --- src/Ryujinx.Graphics.Metal/Texture.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 01513470f..8b742d2cb 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -175,6 +175,7 @@ namespace Ryujinx.Graphics.Metal int height = Info.Height; int depth = Info.Depth; int levels = Info.GetLevelsClamped(); + bool is3D = Info.Target == Target.Texture3D; int offset = 0; @@ -194,7 +195,7 @@ namespace Ryujinx.Graphics.Metal (ulong)offset, (ulong)Info.GetMipStride(level), (ulong)mipSize, - new MTLSize { width = (ulong)width, height = (ulong)height, depth = (ulong)depth }, + new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, MTLTexture, 0, (ulong)level, @@ -205,7 +206,11 @@ namespace Ryujinx.Graphics.Metal width = Math.Max(1, width >> 1); height = Math.Max(1, height >> 1); - depth = Math.Max(1, depth >> 1); + + if (is3D) + { + depth = Math.Max(1, depth >> 1); + } } } -- 2.47.1 From efa9d56a5665f40300ca0fcb363d1907bc0cb05f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 22:58:27 -0400 Subject: [PATCH 087/368] Fix Cubemap & Array Texture Creation --- src/Ryujinx.Graphics.Metal/Texture.cs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 8b742d2cb..b7b004b3e 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -27,18 +27,24 @@ namespace Ryujinx.Graphics.Metal _pipeline = pipeline; _info = info; - var descriptor = new MTLTextureDescriptor - { - PixelFormat = FormatTable.GetFormat(Info.Format), - Usage = MTLTextureUsage.ShaderRead, - Width = (ulong)Width, - Height = (ulong)Height, - Depth = (ulong)Depth - }; - descriptor.Depth = (ulong)Info.Depth; + var descriptor = new MTLTextureDescriptor(); + + descriptor.PixelFormat = FormatTable.GetFormat(Info.Format); + descriptor.Usage = MTLTextureUsage.ShaderRead; descriptor.SampleCount = (ulong)Info.Samples; - descriptor.MipmapLevelCount = (ulong)Info.Levels; descriptor.TextureType = Info.Target.Convert(); + descriptor.Width = (ulong)Info.Width; + descriptor.Height = (ulong)Info.Height; + descriptor.MipmapLevelCount = (ulong)Info.Levels; + + if (info.Target == Target.Texture3D) + { + descriptor.Depth = (ulong)Info.Depth; + } + else if (info.Target != Target.Cubemap) + { + descriptor.ArrayLength = (ulong)Info.Depth; + } var swizzleR = Info.SwizzleR.Convert(); var swizzleG = Info.SwizzleG.Convert(); -- 2.47.1 From e758e531c55b6cb54194a27df7d728f1ad876bd6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 22:58:42 -0400 Subject: [PATCH 088/368] Fix some crashes --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 7 +++++-- .../RenderEncoderState.cs | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index f52b473f8..2cf073d88 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -14,7 +14,8 @@ namespace Ryujinx.Graphics.Metal { Blit, Compute, - Render + Render, + None } [SupportedOSPlatform("macos")] @@ -26,7 +27,7 @@ namespace Ryujinx.Graphics.Metal private MTLCommandBuffer _commandBuffer; private MTLCommandEncoder? _currentEncoder; - private EncoderType _currentEncoderType; + private EncoderType _currentEncoderType = EncoderType.None; private MTLTexture[] _renderTargets = []; private RenderEncoderState _renderEncoderState; @@ -112,6 +113,8 @@ namespace Ryujinx.Graphics.Metal default: throw new ArgumentOutOfRangeException(); } + + _currentEncoderType = EncoderType.None; } } diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index d58fae206..f5e8fe30b 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -18,8 +18,8 @@ namespace Ryujinx.Graphics.Metal private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always; private bool _depthWriteEnabled = false; - private MTLStencilDescriptor? _backFaceStencil = null; - private MTLStencilDescriptor? _frontFaceStencil = null; + private MTLStencilDescriptor _backFaceStencil = new MTLStencilDescriptor(); + private MTLStencilDescriptor _frontFaceStencil = new MTLStencilDescriptor(); public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; @@ -83,8 +83,8 @@ namespace Ryujinx.Graphics.Metal { DepthCompareFunction = _depthCompareFunction, DepthWriteEnabled = _depthWriteEnabled, - BackFaceStencil = _backFaceStencil.Value, - FrontFaceStencil = _frontFaceStencil.Value + BackFaceStencil = _backFaceStencil, + FrontFaceStencil = _frontFaceStencil }); return _depthStencilState.Value; @@ -95,15 +95,17 @@ namespace Ryujinx.Graphics.Metal _depthCompareFunction = depthCompareFunction; _depthWriteEnabled = depthWriteEnabled; - _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + var state = _device.NewDepthStencilState(new MTLDepthStencilDescriptor { DepthCompareFunction = _depthCompareFunction, DepthWriteEnabled = _depthWriteEnabled, - BackFaceStencil = _backFaceStencil.Value, - FrontFaceStencil = _frontFaceStencil.Value + BackFaceStencil = _backFaceStencil, + FrontFaceStencil = _frontFaceStencil }); - return _depthStencilState.Value; + _depthStencilState = state; + + return state; } } } -- 2.47.1 From 36fe41bffdf99c0d65a2f77217e9374b11c3bfc3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 23:09:17 -0400 Subject: [PATCH 089/368] Format --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 3 ++- src/Ryujinx.Graphics.Metal/Texture.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 2cf073d88..3699b8f61 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -640,7 +640,8 @@ namespace Ryujinx.Graphics.Metal var layout = _vertexDescriptor.Layouts.Object((ulong)i); layout.Stride = (ulong)vertexBuffers[i].Stride; - _vertexBuffers[i] = new BufferInfo { + _vertexBuffers[i] = new BufferInfo + { Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(), Offset = vertexBuffers[i].Buffer.Offset }; diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index b7b004b3e..74f70d046 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -56,7 +56,8 @@ namespace Ryujinx.Graphics.Metal info.Format == Format.R5G6B5Unorm) { (swizzleB, swizzleR) = (swizzleR, swizzleB); - } else if (descriptor.PixelFormat == MTLPixelFormat.ABGR4Unorm || info.Format == Format.A1B5G5R5Unorm) + } + else if (descriptor.PixelFormat == MTLPixelFormat.ABGR4Unorm || info.Format == Format.A1B5G5R5Unorm) { var tempB = swizzleB; var tempA = swizzleA; -- 2.47.1 From 65b7af630889dd4ed8c3f8ff5bd982e4d1b8eb73 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 23:12:28 -0400 Subject: [PATCH 090/368] Format --- src/Ryujinx.Graphics.Metal/Texture.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 74f70d046..8cf62aea3 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -27,15 +27,16 @@ namespace Ryujinx.Graphics.Metal _pipeline = pipeline; _info = info; - var descriptor = new MTLTextureDescriptor(); - - descriptor.PixelFormat = FormatTable.GetFormat(Info.Format); - descriptor.Usage = MTLTextureUsage.ShaderRead; - descriptor.SampleCount = (ulong)Info.Samples; - descriptor.TextureType = Info.Target.Convert(); - descriptor.Width = (ulong)Info.Width; - descriptor.Height = (ulong)Info.Height; - descriptor.MipmapLevelCount = (ulong)Info.Levels; + var descriptor = new MTLTextureDescriptor + { + PixelFormat = FormatTable.GetFormat(Info.Format), + Usage = MTLTextureUsage.ShaderRead, + SampleCount = (ulong)Info.Samples, + TextureType = Info.Target.Convert(), + Width = (ulong)Info.Width, + Height = (ulong)Info.Height, + MipmapLevelCount = (ulong)Info.Levels + }; if (info.Target == Target.Texture3D) { -- 2.47.1 From 6a67822b3bb8c2eba760f88f77f0012ea69e33fb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 14:35:19 -0400 Subject: [PATCH 091/368] Set scissors & viewports --- Directory.Packages.props | 2 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 070b31cd6..f859d4532 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 3699b8f61..a3856a016 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -521,11 +521,8 @@ namespace Ryujinx.Graphics.Metal fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) { - // TODO: Fix this function which currently wont accept pointer as intended - if (_currentEncoderType == EncoderType.Render) - { - // new MTLRenderCommandEncoder(_currentEncoder.Value).SetScissorRects(pMtlScissorRects, (ulong)regions.Length); - } + var renderCommandEncoder = GetOrCreateRenderEncoder(); + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissorRects, (ulong)regions.Length); } } @@ -670,11 +667,8 @@ namespace Ryujinx.Graphics.Metal fixed (MTLViewport* pMtlViewports = mtlViewports) { - // TODO: Fix this function which currently wont accept pointer as intended - if (_currentEncoderType == EncoderType.Render) - { - // new MTLRenderCommandEncoder(_currentEncoder.Value).SetViewports(pMtlViewports, (ulong)regions.Length); - } + var renderCommandEncoder = GetOrCreateRenderEncoder(); + renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); } } -- 2.47.1 From e353e3d3fce304b6df0ef0bbc64003f441c916bd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 14:35:35 -0400 Subject: [PATCH 092/368] Dispose pipeline before window --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 0930be9ef..1a04f92e8 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -246,8 +246,8 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { - _window.Dispose(); _pipeline.Dispose(); + _window.Dispose(); } } } -- 2.47.1 From 64e9dcee3dc007b561874fba0ac98aa36b0024b1 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 14:35:47 -0400 Subject: [PATCH 093/368] Fix buffer access syntax --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 2 +- src/Ryujinx.Headless.SDL2/Program.cs.orig | 755 ++++++++++++++++++ 2 files changed, 756 insertions(+), 1 deletion(-) create mode 100644 src/Ryujinx.Headless.SDL2/Program.cs.orig diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 73936383c..2cec9d18a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } StructureField field = buffer.Type.Fields[fieldIndex.Value]; - varName = $"{buffer.Name}.{field.Name}"; + varName = buffer.Name; varType = field.Type; break; diff --git a/src/Ryujinx.Headless.SDL2/Program.cs.orig b/src/Ryujinx.Headless.SDL2/Program.cs.orig new file mode 100644 index 000000000..3dd71bd7c --- /dev/null +++ b/src/Ryujinx.Headless.SDL2/Program.cs.orig @@ -0,0 +1,755 @@ +using CommandLine; +using LibHac.Tools.FsSystem; +using Ryujinx.Audio.Backends.SDL2; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Controller.Motion; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.GraphicsDriver; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Logging.Targets; +using Ryujinx.Common.SystemInterop; +using Ryujinx.Common.Utilities; +using Ryujinx.Cpu; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Multithreading; +using Ryujinx.Graphics.Gpu; +using Ryujinx.Graphics.Gpu.Shader; +using Ryujinx.Graphics.Metal; +using Ryujinx.Graphics.OpenGL; +using Ryujinx.Graphics.Vulkan; +<<<<<<< HEAD +using Ryujinx.Graphics.Vulkan.MoltenVK; +using Ryujinx.Graphics.Metal; +======= +>>>>>>> 137f5970f (Vertex Input Attributes) +using Ryujinx.Headless.SDL2.Metal; +using Ryujinx.Headless.SDL2.OpenGL; +using Ryujinx.Headless.SDL2.Vulkan; +using Ryujinx.HLE; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.Input; +using Ryujinx.Input.HLE; +using Ryujinx.Input.SDL2; +using Ryujinx.SDL2.Common; +using Silk.NET.Vulkan; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Threading; +using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; +using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; +using Key = Ryujinx.Common.Configuration.Hid.Key; + +namespace Ryujinx.Headless.SDL2 +{ + class Program + { + public static string Version { get; private set; } + + private static VirtualFileSystem _virtualFileSystem; + private static ContentManager _contentManager; + private static AccountManager _accountManager; + private static LibHacHorizonManager _libHacHorizonManager; + private static UserChannelPersistence _userChannelPersistence; + private static InputManager _inputManager; + private static Switch _emulationContext; + private static WindowBase _window; + private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; + private static List _inputConfiguration; + private static bool _enableKeyboard; + private static bool _enableMouse; + + private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + + static void Main(string[] args) + { + Version = ReleaseInformation.Version; + + // Make process DPI aware for proper window sizing on high-res screens. + ForceDpiAware.Windows(); + + Console.Title = $"Ryujinx Console {Version} (Headless SDL2)"; + + if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) + { + AutoResetEvent invoked = new(false); + + // MacOS must perform SDL polls from the main thread. + SDL2Driver.MainThreadDispatcher = action => + { + invoked.Reset(); + + WindowBase.QueueMainThreadAction(() => + { + action(); + + invoked.Set(); + }); + + invoked.WaitOne(); + }; + } + + if (OperatingSystem.IsMacOS()) + { + MVKInitialization.InitializeResolver(); + } + + Parser.Default.ParseArguments(args) + .WithParsed(Load) + .WithNotParsed(errors => errors.Output()); + } + + private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) + { + if (inputId == null) + { + if (index == PlayerIndex.Player1) + { + Logger.Info?.Print(LogClass.Application, $"{index} not configured, defaulting to default keyboard."); + + // Default to keyboard + inputId = "0"; + } + else + { + Logger.Info?.Print(LogClass.Application, $"{index} not configured"); + + return null; + } + } + + IGamepad gamepad; + + bool isKeyboard = true; + + gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId); + + if (gamepad == null) + { + gamepad = _inputManager.GamepadDriver.GetGamepad(inputId); + isKeyboard = false; + + if (gamepad == null) + { + Logger.Error?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")"); + + return null; + } + } + + string gamepadName = gamepad.Name; + + gamepad.Dispose(); + + InputConfig config; + + if (inputProfileName == null || inputProfileName.Equals("default")) + { + if (isKeyboard) + { + config = new StandardKeyboardInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.WindowKeyboard, + Id = null, + ControllerType = ControllerType.JoyconPair, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = Key.Up, + DpadDown = Key.Down, + DpadLeft = Key.Left, + DpadRight = Key.Right, + ButtonMinus = Key.Minus, + ButtonL = Key.E, + ButtonZl = Key.Q, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + + LeftJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = Key.W, + StickDown = Key.S, + StickLeft = Key.A, + StickRight = Key.D, + StickButton = Key.F, + }, + + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = Key.Z, + ButtonB = Key.X, + ButtonX = Key.C, + ButtonY = Key.V, + ButtonPlus = Key.Plus, + ButtonR = Key.U, + ButtonZr = Key.O, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + + RightJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = Key.I, + StickDown = Key.K, + StickLeft = Key.J, + StickRight = Key.L, + StickButton = Key.H, + }, + }; + } + else + { + bool isNintendoStyle = gamepadName.Contains("Nintendo"); + + config = new StandardControllerInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.GamepadSDL2, + Id = null, + ControllerType = ControllerType.JoyconPair, + DeadzoneLeft = 0.1f, + DeadzoneRight = 0.1f, + RangeLeft = 1.0f, + RangeRight = 1.0f, + TriggerThreshold = 0.5f, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = ConfigGamepadInputId.DpadUp, + DpadDown = ConfigGamepadInputId.DpadDown, + DpadLeft = ConfigGamepadInputId.DpadLeft, + DpadRight = ConfigGamepadInputId.DpadRight, + ButtonMinus = ConfigGamepadInputId.Minus, + ButtonL = ConfigGamepadInputId.LeftShoulder, + ButtonZl = ConfigGamepadInputId.LeftTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, + }, + + LeftJoyconStick = new JoyconConfigControllerStick + { + Joystick = ConfigStickInputId.Left, + StickButton = ConfigGamepadInputId.LeftStick, + InvertStickX = false, + InvertStickY = false, + Rotate90CW = false, + }, + + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, + ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, + ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, + ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, + ButtonPlus = ConfigGamepadInputId.Plus, + ButtonR = ConfigGamepadInputId.RightShoulder, + ButtonZr = ConfigGamepadInputId.RightTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, + }, + + RightJoyconStick = new JoyconConfigControllerStick + { + Joystick = ConfigStickInputId.Right, + StickButton = ConfigGamepadInputId.RightStick, + InvertStickX = false, + InvertStickY = false, + Rotate90CW = false, + }, + + Motion = new StandardMotionConfigController + { + MotionBackend = MotionInputBackendType.GamepadDriver, + EnableMotion = true, + Sensitivity = 100, + GyroDeadzone = 1, + }, + Rumble = new RumbleConfigController + { + StrongRumble = 1f, + WeakRumble = 1f, + EnableRumble = false, + }, + }; + } + } + else + { + string profileBasePath; + + if (isKeyboard) + { + profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "keyboard"); + } + else + { + profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "controller"); + } + + string path = Path.Combine(profileBasePath, inputProfileName + ".json"); + + if (!File.Exists(path)) + { + Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" not found for \"{inputId}\""); + + return null; + } + + try + { + config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); + } + catch (JsonException) + { + Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" parsing failed for \"{inputId}\""); + + return null; + } + } + + config.Id = inputId; + config.PlayerIndex = index; + + string inputTypeName = isKeyboard ? "Keyboard" : "Gamepad"; + + Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} configured with {inputTypeName} \"{config.Id}\""); + + // If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0. + if (config is StandardControllerInputConfig controllerConfig) + { + if (controllerConfig.RangeLeft <= 0.0f && controllerConfig.RangeRight <= 0.0f) + { + controllerConfig.RangeLeft = 1.0f; + controllerConfig.RangeRight = 1.0f; + + Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); + } + } + + return config; + } + + static void Load(Options option) + { + AppDataManager.Initialize(option.BaseDataDir); + + _virtualFileSystem = VirtualFileSystem.CreateInstance(); + _libHacHorizonManager = new LibHacHorizonManager(); + + _libHacHorizonManager.InitializeFsServer(_virtualFileSystem); + _libHacHorizonManager.InitializeArpServer(); + _libHacHorizonManager.InitializeBcatServer(); + _libHacHorizonManager.InitializeSystemClients(); + + _contentManager = new ContentManager(_virtualFileSystem); + _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile); + _userChannelPersistence = new UserChannelPersistence(); + + _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); + + GraphicsConfig.EnableShaderCache = true; + + if (OperatingSystem.IsMacOS()) + { + if (option.GraphicsBackend == GraphicsBackend.OpenGl) + { + option.GraphicsBackend = GraphicsBackend.Vulkan; + Logger.Warning?.Print(LogClass.Application, "OpenGL is not supported on macOS, switching to Vulkan!"); + } + } + + IGamepad gamepad; + + if (option.ListInputIds) + { + Logger.Info?.Print(LogClass.Application, "Input Ids:"); + + foreach (string id in _inputManager.KeyboardDriver.GamepadsIds) + { + gamepad = _inputManager.KeyboardDriver.GetGamepad(id); + + Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")"); + + gamepad.Dispose(); + } + + foreach (string id in _inputManager.GamepadDriver.GamepadsIds) + { + gamepad = _inputManager.GamepadDriver.GetGamepad(id); + + Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")"); + + gamepad.Dispose(); + } + + return; + } + + if (option.InputPath == null) + { + Logger.Error?.Print(LogClass.Application, "Please provide a file to load"); + + return; + } + + _inputConfiguration = new List(); + _enableKeyboard = option.EnableKeyboard; + _enableMouse = option.EnableMouse; + + static void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) + { + InputConfig inputConfig = HandlePlayerConfiguration(inputProfileName, inputId, index); + + if (inputConfig != null) + { + _inputConfiguration.Add(inputConfig); + } + } + + LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); + LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); + LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3); + LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4); + LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5); + LoadPlayerConfiguration(option.InputProfile6Name, option.InputId6, PlayerIndex.Player6); + LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7); + LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8); + LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld); + + if (_inputConfiguration.Count == 0) + { + return; + } + + // Setup logging level + Logger.SetEnable(LogLevel.Debug, option.LoggingEnableDebug); + Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub); + Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo); + Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning); + Logger.SetEnable(LogLevel.Error, option.LoggingEnableError); + Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace); + Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest); + Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog); + + if (!option.DisableFileLog) + { + string logDir = AppDataManager.LogsDirPath; + FileStream logFile = null; + + if (!string.IsNullOrEmpty(logDir)) + { + logFile = FileLogTarget.PrepareLogFile(logDir); + } + + if (logFile != null) + { + Logger.AddTarget(new AsyncLogTargetWrapper( + new FileLogTarget("file", logFile), + 1000, + AsyncLogTargetOverflowAction.Block + )); + } + else + { + Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the Logs directory, Application Data, or the Ryujinx directory is writable."); + } + } + + // Setup graphics configuration + GraphicsConfig.EnableShaderCache = !option.DisableShaderCache; + GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression; + GraphicsConfig.ResScale = option.ResScale; + GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy; + GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath; + GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE; + + DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off); + + while (true) + { + LoadApplication(option); + + if (_userChannelPersistence.PreviousIndex == -1 || !_userChannelPersistence.ShouldRestart) + { + break; + } + + _userChannelPersistence.ShouldRestart = false; + } + + _inputManager.Dispose(); + } + + private static void SetupProgressHandler() + { + if (_emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) + { + _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler; + _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler; + } + + _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; + _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; + } + + private static void ProgressHandler(T state, int current, int total) where T : Enum + { + string label = state switch + { + LoadState => $"PTC : {current}/{total}", + ShaderCacheState => $"Shaders : {current}/{total}", + _ => throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}"), + }; + + Logger.Info?.Print(LogClass.Application, label); + } + + private static WindowBase CreateWindow(Options options) + { + return options.GraphicsBackend switch + { + GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode), + GraphicsBackend.Metal => new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode), + _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode) + }; + } + + private static IRenderer CreateRenderer(Options options, WindowBase window) + { + if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) + { + string preferredGpuId = string.Empty; + Vk api = Vk.GetApi(); + + if (!string.IsNullOrEmpty(options.PreferredGPUVendor)) + { + string preferredGpuVendor = options.PreferredGPUVendor.ToLowerInvariant(); + var devices = VulkanRenderer.GetPhysicalDevices(api); + + foreach (var device in devices) + { + if (device.Vendor.ToLowerInvariant() == preferredGpuVendor) + { + preferredGpuId = device.Id; + break; + } + } + } + + return new VulkanRenderer( + api, + (instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))), + vulkanWindow.GetRequiredInstanceExtensions, + preferredGpuId); + } + + if (options.GraphicsBackend == GraphicsBackend.Metal && window is MetalWindow metalWindow && OperatingSystem.IsMacOS()) + { + return new MetalRenderer(metalWindow.GetLayer); + } + + return new OpenGLRenderer(); + } + + private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) + { + BackendThreading threadingMode = options.BackendThreading; + + bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); + + if (threadedGAL) + { + renderer = new ThreadedRenderer(renderer); + } + + HLEConfiguration configuration = new(_virtualFileSystem, + _libHacHorizonManager, + _contentManager, + _accountManager, + _userChannelPersistence, + renderer, + new SDL2HardwareDeviceDriver(), + options.ExpandRAM ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, + window, + options.SystemLanguage, + options.SystemRegion, + !options.DisableVSync, + !options.DisableDockedMode, + !options.DisablePTC, + options.EnableInternetAccess, + !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, + options.FsGlobalAccessLogMode, + options.SystemTimeOffset, + options.SystemTimeZone, + options.MemoryManagerMode, + options.IgnoreMissingServices, + options.AspectRatio, + options.AudioVolume, + options.UseHypervisor ?? true, + options.MultiplayerLanInterfaceId, + Common.Configuration.Multiplayer.MultiplayerMode.Disabled); + + return new Switch(configuration); + } + + private static void ExecutionEntrypoint() + { + if (OperatingSystem.IsWindows()) + { + _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); + } + + DisplaySleep.Prevent(); + + _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse); + + _window.Execute(); + + _emulationContext.Dispose(); + _window.Dispose(); + + if (OperatingSystem.IsWindows()) + { + _windowsMultimediaTimerResolution?.Dispose(); + _windowsMultimediaTimerResolution = null; + } + } + + private static bool LoadApplication(Options options) + { + string path = options.InputPath; + + Logger.RestartTime(); + + WindowBase window = CreateWindow(options); + IRenderer renderer = CreateRenderer(options, window); + + _window = window; + + _window.IsFullscreen = options.IsFullscreen; + _window.DisplayId = options.DisplayId; + _window.IsExclusiveFullscreen = options.IsExclusiveFullscreen; + _window.ExclusiveFullscreenWidth = options.ExclusiveFullscreenWidth; + _window.ExclusiveFullscreenHeight = options.ExclusiveFullscreenHeight; + _window.AntiAliasing = options.AntiAliasing; + _window.ScalingFilter = options.ScalingFilter; + _window.ScalingFilterLevel = options.ScalingFilterLevel; + + _emulationContext = InitializeEmulationContext(window, renderer, options); + + SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); + + Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); + + if (Directory.Exists(path)) + { + string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); + + if (romFsFiles.Length == 0) + { + romFsFiles = Directory.GetFiles(path, "*.romfs"); + } + + if (romFsFiles.Length > 0) + { + Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); + + if (!_emulationContext.LoadCart(path, romFsFiles[0])) + { + _emulationContext.Dispose(); + + return false; + } + } + else + { + Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); + + if (!_emulationContext.LoadCart(path)) + { + _emulationContext.Dispose(); + + return false; + } + } + } + else if (File.Exists(path)) + { + switch (Path.GetExtension(path).ToLowerInvariant()) + { + case ".xci": + Logger.Info?.Print(LogClass.Application, "Loading as XCI."); + + if (!_emulationContext.LoadXci(path)) + { + _emulationContext.Dispose(); + + return false; + } + break; + case ".nca": + Logger.Info?.Print(LogClass.Application, "Loading as NCA."); + + if (!_emulationContext.LoadNca(path)) + { + _emulationContext.Dispose(); + + return false; + } + break; + case ".nsp": + case ".pfs0": + Logger.Info?.Print(LogClass.Application, "Loading as NSP."); + + if (!_emulationContext.LoadNsp(path)) + { + _emulationContext.Dispose(); + + return false; + } + break; + default: + Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); + try + { + if (!_emulationContext.LoadProgram(path)) + { + _emulationContext.Dispose(); + + return false; + } + } + catch (ArgumentOutOfRangeException) + { + Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); + + _emulationContext.Dispose(); + + return false; + } + break; + } + } + else + { + Logger.Warning?.Print(LogClass.Application, $"Couldn't load '{options.InputPath}'. Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); + + _emulationContext.Dispose(); + + return false; + } + + SetupProgressHandler(); + ExecutionEntrypoint(); + + return true; + } + } +} -- 2.47.1 From 0df70db73c89f26eec7fe21181831c6e1ea1a1e8 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 21:21:14 -0600 Subject: [PATCH 094/368] remnant --- src/Ryujinx.Headless.SDL2/Program.cs.orig | 755 ---------------------- 1 file changed, 755 deletions(-) delete mode 100644 src/Ryujinx.Headless.SDL2/Program.cs.orig diff --git a/src/Ryujinx.Headless.SDL2/Program.cs.orig b/src/Ryujinx.Headless.SDL2/Program.cs.orig deleted file mode 100644 index 3dd71bd7c..000000000 --- a/src/Ryujinx.Headless.SDL2/Program.cs.orig +++ /dev/null @@ -1,755 +0,0 @@ -using CommandLine; -using LibHac.Tools.FsSystem; -using Ryujinx.Audio.Backends.SDL2; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using Ryujinx.Common.Configuration.Hid.Keyboard; -using Ryujinx.Common.GraphicsDriver; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Logging.Targets; -using Ryujinx.Common.SystemInterop; -using Ryujinx.Common.Utilities; -using Ryujinx.Cpu; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.Graphics.Gpu; -using Ryujinx.Graphics.Gpu.Shader; -using Ryujinx.Graphics.Metal; -using Ryujinx.Graphics.OpenGL; -using Ryujinx.Graphics.Vulkan; -<<<<<<< HEAD -using Ryujinx.Graphics.Vulkan.MoltenVK; -using Ryujinx.Graphics.Metal; -======= ->>>>>>> 137f5970f (Vertex Input Attributes) -using Ryujinx.Headless.SDL2.Metal; -using Ryujinx.Headless.SDL2.OpenGL; -using Ryujinx.Headless.SDL2.Vulkan; -using Ryujinx.HLE; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.Input; -using Ryujinx.Input.HLE; -using Ryujinx.Input.SDL2; -using Ryujinx.SDL2.Common; -using Silk.NET.Vulkan; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using System.Threading; -using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; -using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; -using Key = Ryujinx.Common.Configuration.Hid.Key; - -namespace Ryujinx.Headless.SDL2 -{ - class Program - { - public static string Version { get; private set; } - - private static VirtualFileSystem _virtualFileSystem; - private static ContentManager _contentManager; - private static AccountManager _accountManager; - private static LibHacHorizonManager _libHacHorizonManager; - private static UserChannelPersistence _userChannelPersistence; - private static InputManager _inputManager; - private static Switch _emulationContext; - private static WindowBase _window; - private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; - private static List _inputConfiguration; - private static bool _enableKeyboard; - private static bool _enableMouse; - - private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - static void Main(string[] args) - { - Version = ReleaseInformation.Version; - - // Make process DPI aware for proper window sizing on high-res screens. - ForceDpiAware.Windows(); - - Console.Title = $"Ryujinx Console {Version} (Headless SDL2)"; - - if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) - { - AutoResetEvent invoked = new(false); - - // MacOS must perform SDL polls from the main thread. - SDL2Driver.MainThreadDispatcher = action => - { - invoked.Reset(); - - WindowBase.QueueMainThreadAction(() => - { - action(); - - invoked.Set(); - }); - - invoked.WaitOne(); - }; - } - - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - } - - Parser.Default.ParseArguments(args) - .WithParsed(Load) - .WithNotParsed(errors => errors.Output()); - } - - private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) - { - if (inputId == null) - { - if (index == PlayerIndex.Player1) - { - Logger.Info?.Print(LogClass.Application, $"{index} not configured, defaulting to default keyboard."); - - // Default to keyboard - inputId = "0"; - } - else - { - Logger.Info?.Print(LogClass.Application, $"{index} not configured"); - - return null; - } - } - - IGamepad gamepad; - - bool isKeyboard = true; - - gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId); - - if (gamepad == null) - { - gamepad = _inputManager.GamepadDriver.GetGamepad(inputId); - isKeyboard = false; - - if (gamepad == null) - { - Logger.Error?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")"); - - return null; - } - } - - string gamepadName = gamepad.Name; - - gamepad.Dispose(); - - InputConfig config; - - if (inputProfileName == null || inputProfileName.Equals("default")) - { - if (isKeyboard) - { - config = new StandardKeyboardInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = null, - ControllerType = ControllerType.JoyconPair, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - - LeftJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, - }, - - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - }, - }; - } - else - { - bool isNintendoStyle = gamepadName.Contains("Nintendo"); - - config = new StandardControllerInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.GamepadSDL2, - Id = null, - ControllerType = ControllerType.JoyconPair, - DeadzoneLeft = 0.1f, - DeadzoneRight = 0.1f, - RangeLeft = 1.0f, - RangeRight = 1.0f, - TriggerThreshold = 0.5f, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = ConfigGamepadInputId.DpadUp, - DpadDown = ConfigGamepadInputId.DpadDown, - DpadLeft = ConfigGamepadInputId.DpadLeft, - DpadRight = ConfigGamepadInputId.DpadRight, - ButtonMinus = ConfigGamepadInputId.Minus, - ButtonL = ConfigGamepadInputId.LeftShoulder, - ButtonZl = ConfigGamepadInputId.LeftTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - - LeftJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Left, - StickButton = ConfigGamepadInputId.LeftStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = false, - }, - - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, - ButtonPlus = ConfigGamepadInputId.Plus, - ButtonR = ConfigGamepadInputId.RightShoulder, - ButtonZr = ConfigGamepadInputId.RightTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - - RightJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Right, - StickButton = ConfigGamepadInputId.RightStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = false, - }, - - Motion = new StandardMotionConfigController - { - MotionBackend = MotionInputBackendType.GamepadDriver, - EnableMotion = true, - Sensitivity = 100, - GyroDeadzone = 1, - }, - Rumble = new RumbleConfigController - { - StrongRumble = 1f, - WeakRumble = 1f, - EnableRumble = false, - }, - }; - } - } - else - { - string profileBasePath; - - if (isKeyboard) - { - profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "keyboard"); - } - else - { - profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "controller"); - } - - string path = Path.Combine(profileBasePath, inputProfileName + ".json"); - - if (!File.Exists(path)) - { - Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" not found for \"{inputId}\""); - - return null; - } - - try - { - config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); - } - catch (JsonException) - { - Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" parsing failed for \"{inputId}\""); - - return null; - } - } - - config.Id = inputId; - config.PlayerIndex = index; - - string inputTypeName = isKeyboard ? "Keyboard" : "Gamepad"; - - Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} configured with {inputTypeName} \"{config.Id}\""); - - // If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0. - if (config is StandardControllerInputConfig controllerConfig) - { - if (controllerConfig.RangeLeft <= 0.0f && controllerConfig.RangeRight <= 0.0f) - { - controllerConfig.RangeLeft = 1.0f; - controllerConfig.RangeRight = 1.0f; - - Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); - } - } - - return config; - } - - static void Load(Options option) - { - AppDataManager.Initialize(option.BaseDataDir); - - _virtualFileSystem = VirtualFileSystem.CreateInstance(); - _libHacHorizonManager = new LibHacHorizonManager(); - - _libHacHorizonManager.InitializeFsServer(_virtualFileSystem); - _libHacHorizonManager.InitializeArpServer(); - _libHacHorizonManager.InitializeBcatServer(); - _libHacHorizonManager.InitializeSystemClients(); - - _contentManager = new ContentManager(_virtualFileSystem); - _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile); - _userChannelPersistence = new UserChannelPersistence(); - - _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); - - GraphicsConfig.EnableShaderCache = true; - - if (OperatingSystem.IsMacOS()) - { - if (option.GraphicsBackend == GraphicsBackend.OpenGl) - { - option.GraphicsBackend = GraphicsBackend.Vulkan; - Logger.Warning?.Print(LogClass.Application, "OpenGL is not supported on macOS, switching to Vulkan!"); - } - } - - IGamepad gamepad; - - if (option.ListInputIds) - { - Logger.Info?.Print(LogClass.Application, "Input Ids:"); - - foreach (string id in _inputManager.KeyboardDriver.GamepadsIds) - { - gamepad = _inputManager.KeyboardDriver.GetGamepad(id); - - Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")"); - - gamepad.Dispose(); - } - - foreach (string id in _inputManager.GamepadDriver.GamepadsIds) - { - gamepad = _inputManager.GamepadDriver.GetGamepad(id); - - Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")"); - - gamepad.Dispose(); - } - - return; - } - - if (option.InputPath == null) - { - Logger.Error?.Print(LogClass.Application, "Please provide a file to load"); - - return; - } - - _inputConfiguration = new List(); - _enableKeyboard = option.EnableKeyboard; - _enableMouse = option.EnableMouse; - - static void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) - { - InputConfig inputConfig = HandlePlayerConfiguration(inputProfileName, inputId, index); - - if (inputConfig != null) - { - _inputConfiguration.Add(inputConfig); - } - } - - LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); - LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); - LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3); - LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4); - LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5); - LoadPlayerConfiguration(option.InputProfile6Name, option.InputId6, PlayerIndex.Player6); - LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7); - LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8); - LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld); - - if (_inputConfiguration.Count == 0) - { - return; - } - - // Setup logging level - Logger.SetEnable(LogLevel.Debug, option.LoggingEnableDebug); - Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub); - Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo); - Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning); - Logger.SetEnable(LogLevel.Error, option.LoggingEnableError); - Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace); - Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest); - Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog); - - if (!option.DisableFileLog) - { - string logDir = AppDataManager.LogsDirPath; - FileStream logFile = null; - - if (!string.IsNullOrEmpty(logDir)) - { - logFile = FileLogTarget.PrepareLogFile(logDir); - } - - if (logFile != null) - { - Logger.AddTarget(new AsyncLogTargetWrapper( - new FileLogTarget("file", logFile), - 1000, - AsyncLogTargetOverflowAction.Block - )); - } - else - { - Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the Logs directory, Application Data, or the Ryujinx directory is writable."); - } - } - - // Setup graphics configuration - GraphicsConfig.EnableShaderCache = !option.DisableShaderCache; - GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression; - GraphicsConfig.ResScale = option.ResScale; - GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy; - GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath; - GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE; - - DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off); - - while (true) - { - LoadApplication(option); - - if (_userChannelPersistence.PreviousIndex == -1 || !_userChannelPersistence.ShouldRestart) - { - break; - } - - _userChannelPersistence.ShouldRestart = false; - } - - _inputManager.Dispose(); - } - - private static void SetupProgressHandler() - { - if (_emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) - { - _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler; - _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler; - } - - _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; - _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; - } - - private static void ProgressHandler(T state, int current, int total) where T : Enum - { - string label = state switch - { - LoadState => $"PTC : {current}/{total}", - ShaderCacheState => $"Shaders : {current}/{total}", - _ => throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}"), - }; - - Logger.Info?.Print(LogClass.Application, label); - } - - private static WindowBase CreateWindow(Options options) - { - return options.GraphicsBackend switch - { - GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode), - GraphicsBackend.Metal => new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode), - _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode) - }; - } - - private static IRenderer CreateRenderer(Options options, WindowBase window) - { - if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) - { - string preferredGpuId = string.Empty; - Vk api = Vk.GetApi(); - - if (!string.IsNullOrEmpty(options.PreferredGPUVendor)) - { - string preferredGpuVendor = options.PreferredGPUVendor.ToLowerInvariant(); - var devices = VulkanRenderer.GetPhysicalDevices(api); - - foreach (var device in devices) - { - if (device.Vendor.ToLowerInvariant() == preferredGpuVendor) - { - preferredGpuId = device.Id; - break; - } - } - } - - return new VulkanRenderer( - api, - (instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))), - vulkanWindow.GetRequiredInstanceExtensions, - preferredGpuId); - } - - if (options.GraphicsBackend == GraphicsBackend.Metal && window is MetalWindow metalWindow && OperatingSystem.IsMacOS()) - { - return new MetalRenderer(metalWindow.GetLayer); - } - - return new OpenGLRenderer(); - } - - private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) - { - BackendThreading threadingMode = options.BackendThreading; - - bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - - if (threadedGAL) - { - renderer = new ThreadedRenderer(renderer); - } - - HLEConfiguration configuration = new(_virtualFileSystem, - _libHacHorizonManager, - _contentManager, - _accountManager, - _userChannelPersistence, - renderer, - new SDL2HardwareDeviceDriver(), - options.ExpandRAM ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, - window, - options.SystemLanguage, - options.SystemRegion, - !options.DisableVSync, - !options.DisableDockedMode, - !options.DisablePTC, - options.EnableInternetAccess, - !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, - options.FsGlobalAccessLogMode, - options.SystemTimeOffset, - options.SystemTimeZone, - options.MemoryManagerMode, - options.IgnoreMissingServices, - options.AspectRatio, - options.AudioVolume, - options.UseHypervisor ?? true, - options.MultiplayerLanInterfaceId, - Common.Configuration.Multiplayer.MultiplayerMode.Disabled); - - return new Switch(configuration); - } - - private static void ExecutionEntrypoint() - { - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); - } - - DisplaySleep.Prevent(); - - _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse); - - _window.Execute(); - - _emulationContext.Dispose(); - _window.Dispose(); - - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution?.Dispose(); - _windowsMultimediaTimerResolution = null; - } - } - - private static bool LoadApplication(Options options) - { - string path = options.InputPath; - - Logger.RestartTime(); - - WindowBase window = CreateWindow(options); - IRenderer renderer = CreateRenderer(options, window); - - _window = window; - - _window.IsFullscreen = options.IsFullscreen; - _window.DisplayId = options.DisplayId; - _window.IsExclusiveFullscreen = options.IsExclusiveFullscreen; - _window.ExclusiveFullscreenWidth = options.ExclusiveFullscreenWidth; - _window.ExclusiveFullscreenHeight = options.ExclusiveFullscreenHeight; - _window.AntiAliasing = options.AntiAliasing; - _window.ScalingFilter = options.ScalingFilter; - _window.ScalingFilterLevel = options.ScalingFilterLevel; - - _emulationContext = InitializeEmulationContext(window, renderer, options); - - SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); - - Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); - - if (Directory.Exists(path)) - { - string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); - - if (romFsFiles.Length == 0) - { - romFsFiles = Directory.GetFiles(path, "*.romfs"); - } - - if (romFsFiles.Length > 0) - { - Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); - - if (!_emulationContext.LoadCart(path, romFsFiles[0])) - { - _emulationContext.Dispose(); - - return false; - } - } - else - { - Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); - - if (!_emulationContext.LoadCart(path)) - { - _emulationContext.Dispose(); - - return false; - } - } - } - else if (File.Exists(path)) - { - switch (Path.GetExtension(path).ToLowerInvariant()) - { - case ".xci": - Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - - if (!_emulationContext.LoadXci(path)) - { - _emulationContext.Dispose(); - - return false; - } - break; - case ".nca": - Logger.Info?.Print(LogClass.Application, "Loading as NCA."); - - if (!_emulationContext.LoadNca(path)) - { - _emulationContext.Dispose(); - - return false; - } - break; - case ".nsp": - case ".pfs0": - Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - - if (!_emulationContext.LoadNsp(path)) - { - _emulationContext.Dispose(); - - return false; - } - break; - default: - Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); - try - { - if (!_emulationContext.LoadProgram(path)) - { - _emulationContext.Dispose(); - - return false; - } - } - catch (ArgumentOutOfRangeException) - { - Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); - - _emulationContext.Dispose(); - - return false; - } - break; - } - } - else - { - Logger.Warning?.Print(LogClass.Application, $"Couldn't load '{options.InputPath}'. Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); - - _emulationContext.Dispose(); - - return false; - } - - SetupProgressHandler(); - ExecutionEntrypoint(); - - return true; - } - } -} -- 2.47.1 From 44bd12104bfdfaa312cfa2f474e1e7ea3b13cd1f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 16:37:08 -0400 Subject: [PATCH 095/368] Bind Uniform & Storage Buffers --- src/Ryujinx.Graphics.Metal/BufferInfo.cs | 1 + src/Ryujinx.Graphics.Metal/Pipeline.cs | 89 ++++++++++++++++++++---- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/BufferInfo.cs b/src/Ryujinx.Graphics.Metal/BufferInfo.cs index 72deca3d8..b4a1b2cb5 100644 --- a/src/Ryujinx.Graphics.Metal/BufferInfo.cs +++ b/src/Ryujinx.Graphics.Metal/BufferInfo.cs @@ -6,5 +6,6 @@ namespace Ryujinx.Graphics.Metal { public IntPtr Handle; public int Offset; + public int Index; } } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index a3856a016..2eb0340a5 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -5,6 +5,7 @@ using SharpMetal.Foundation; using SharpMetal.Metal; using SharpMetal.QuartzCore; using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -32,7 +33,9 @@ namespace Ryujinx.Graphics.Metal private RenderEncoderState _renderEncoderState; private readonly MTLVertexDescriptor _vertexDescriptor = new(); - private BufferInfo[] _vertexBuffers = []; + private List _vertexBuffers = []; + private List _uniformBuffers = []; + private List _storageBuffers = []; private MTLBuffer _indexBuffer; private MTLIndexType _indexType; @@ -136,10 +139,7 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); - for (int i = 0; i < _vertexBuffers.Length; i++) - { - renderCommandEncoder.SetVertexBuffer(new MTLBuffer(_vertexBuffers[i].Handle), (ulong)_vertexBuffers[i].Offset, (ulong)i); - } + RebindBuffers(renderCommandEncoder); _currentEncoder = renderCommandEncoder; _currentEncoderType = EncoderType.Render; @@ -219,6 +219,26 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } + public void RebindBuffers(MTLRenderCommandEncoder renderCommandEncoder) + { + foreach (var vertexBuffer in _vertexBuffers) + { + renderCommandEncoder.SetVertexBuffer(new MTLBuffer(vertexBuffer.Handle), (ulong)vertexBuffer.Offset, (ulong)vertexBuffer.Index); + } + + foreach (var uniformBuffer in _uniformBuffers) + { + renderCommandEncoder.SetVertexBuffer(new MTLBuffer(uniformBuffer.Handle), (ulong)uniformBuffer.Offset, (ulong)uniformBuffer.Index); + renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(uniformBuffer.Handle), (ulong)uniformBuffer.Offset, (ulong)uniformBuffer.Index); + } + + foreach (var storageBuffer in _storageBuffers) + { + renderCommandEncoder.SetVertexBuffer(new MTLBuffer(storageBuffer.Handle), (ulong)storageBuffer.Offset, (ulong)storageBuffer.Index); + renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(storageBuffer.Handle), (ulong)storageBuffer.Offset, (ulong)storageBuffer.Index); + } + } + public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) { var blitCommandEncoder = GetOrCreateBlitEncoder(); @@ -333,7 +353,7 @@ namespace Ryujinx.Graphics.Metal public void SetBlendState(AdvancedBlendDescriptor blend) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + Logger.Warning?.Print(LogClass.Gpu, "Advanced blend is not supported in Metal!"); } public void SetBlendState(int index, BlendDescriptor blend) @@ -558,7 +578,26 @@ namespace Ryujinx.Graphics.Metal public void SetStorageBuffers(ReadOnlySpan buffers) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _storageBuffers = []; + + foreach (BufferAssignment buffer in buffers) + { + if (buffer.Range.Size != 0) + { + _storageBuffers.Add(new BufferInfo + { + Handle = buffer.Range.Handle.ToIntPtr(), + Offset = buffer.Range.Offset, + Index = buffer.Binding + }); + } + } + + if (_currentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + RebindBuffers(renderCommandEncoder); + } } public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) @@ -600,7 +639,26 @@ namespace Ryujinx.Graphics.Metal public void SetUniformBuffers(ReadOnlySpan buffers) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _uniformBuffers = []; + + foreach (BufferAssignment buffer in buffers) + { + if (buffer.Range.Size != 0) + { + _uniformBuffers.Add(new BufferInfo + { + Handle = buffer.Range.Handle.ToIntPtr(), + Offset = buffer.Range.Offset, + Index = buffer.Binding + }); + } + } + + if (_currentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + RebindBuffers(renderCommandEncoder); + } } public void SetUserClipDistance(int index, bool enableClip) @@ -628,7 +686,7 @@ namespace Ryujinx.Graphics.Metal public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - _vertexBuffers = new BufferInfo[vertexBuffers.Length]; + _vertexBuffers = []; for (int i = 0; i < vertexBuffers.Length; i++) { @@ -637,13 +695,20 @@ namespace Ryujinx.Graphics.Metal var layout = _vertexDescriptor.Layouts.Object((ulong)i); layout.Stride = (ulong)vertexBuffers[i].Stride; - _vertexBuffers[i] = new BufferInfo + _vertexBuffers.Add(new BufferInfo { Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(), - Offset = vertexBuffers[i].Buffer.Offset - }; + Offset = vertexBuffers[i].Buffer.Offset, + Index = i + }); } } + + if (_currentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + RebindBuffers(renderCommandEncoder); + } } public unsafe void SetViewports(ReadOnlySpan viewports) -- 2.47.1 From ebd2d82ff3e7207f246152ed92b7a0c053fd07da Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 18:16:11 -0400 Subject: [PATCH 096/368] =?UTF-8?q?Buffer=20bindings=20in=20shader?= =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will need to be reworked --- .../CodeGen/Msl/MslGenerator.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 5e6f344fc..8fa9df0ca 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -24,13 +24,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl if (info.Functions.Count != 0) { - for (int i = 1; i < info.Functions.Count; i++) - { - context.AppendLine($"{GetFunctionSignature(context, info.Functions[i], parameters.Definitions.Stage)};"); - } - - context.AppendLine(); - for (int i = 1; i < info.Functions.Count; i++) { PrintFunction(context, info.Functions[i], parameters.Definitions.Stage); @@ -58,7 +51,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.LeaveScope(); } - private static string GetFunctionSignature(CodeGenContext context, StructuredFunction function, ShaderStage stage, bool isMainFunc = false) + private static string GetFunctionSignature( + CodeGenContext context, + StructuredFunction function, + ShaderStage stage, + bool isMainFunc = false) { string[] args = new string[function.InArguments.Length + function.OutArguments.Length]; @@ -115,6 +112,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl args = args.Prepend("KernelIn in [[stage_in]]").ToArray(); } } + + foreach (var constantBuffer in context.Properties.ConstantBuffers.Values) + { + args = args.Append($"constant float4 *{constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); + } + + foreach (var storageBuffers in context.Properties.StorageBuffers.Values) + { + args = args.Append($"device float4 *{storageBuffers.Name} [[buffer({storageBuffers.Binding})]]").ToArray(); + } } return $"{funcKeyword} {returnType} {funcName ?? function.Name}({string.Join(", ", args)})"; -- 2.47.1 From 60c99e32b060114e50a540a2852d535fa5ba2426 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 21 Mar 2024 11:35:01 -0400 Subject: [PATCH 097/368] Require Argument Buffers Tier 2 --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 1a04f92e8..d1f321c2e 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -29,6 +29,12 @@ namespace Ryujinx.Graphics.Metal public MetalRenderer(Func metalLayer) { _device = MTLDevice.CreateSystemDefaultDevice(); + + if (_device.ArgumentBuffersSupport != MTLArgumentBuffersTier.Tier2) + { + throw new NotSupportedException("Metal backend requires Tier 2 Argument Buffer support."); + } + _queue = _device.NewCommandQueue(); _getMetalLayer = metalLayer; } -- 2.47.1 From 2f70337dca3b1d3caa4d9af7e8ae275c54cfea31 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 21 Mar 2024 11:44:45 -0400 Subject: [PATCH 098/368] Fix Scissor/Viewport state & Validation Error --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 32 +++++++++++++------ .../RenderEncoderState.cs | 32 ++++++++++++++++++- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 2eb0340a5..327bd849f 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -524,12 +524,17 @@ namespace Ryujinx.Graphics.Metal public unsafe void SetScissors(ReadOnlySpan> regions) { - // TODO: Test max allowed scissor rects on device - var mtlScissorRects = new MTLScissorRect[regions.Length]; + int maxScissors = Math.Min(regions.Length, _renderEncoderState.ViewportCount); - for (int i = 0; i < regions.Length; i++) + if (maxScissors == 0) { return; } + + // TODO: Test max allowed scissor rects on device + var mtlScissorRects = new MTLScissorRect[maxScissors]; + + for (int i = 0; i < maxScissors; i++) { var region = regions[i]; + mtlScissorRects[i] = new MTLScissorRect { height = (ulong)region.Height, @@ -539,10 +544,13 @@ namespace Ryujinx.Graphics.Metal }; } - fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissorRects, (ulong)regions.Length); + _renderEncoderState.UpdateScissors(mtlScissorRects); + if (_currentEncoderType == EncoderType.Render) { + fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissorRects, (ulong)regions.Length); + } } } @@ -730,10 +738,14 @@ namespace Ryujinx.Graphics.Metal }; } - fixed (MTLViewport* pMtlViewports = mtlViewports) + _renderEncoderState.UpdateViewport(mtlViewports); + if (_currentEncoderType == EncoderType.Render) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); + fixed (MTLViewport* pMtlViewports = mtlViewports) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); + } } } diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index f5e8fe30b..4a045c96b 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -25,6 +25,10 @@ namespace Ryujinx.Graphics.Metal public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.Clockwise; + private MTLViewport[] _viewports = []; + private MTLScissorRect[] _scissors = []; + public int ViewportCount => _viewports.Length; + public RenderEncoderState(MTLFunction vertexFunction, MTLFunction fragmentFunction, MTLDevice device) { _vertexFunction = vertexFunction; @@ -32,7 +36,7 @@ namespace Ryujinx.Graphics.Metal _device = device; } - public readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLVertexDescriptor vertexDescriptor) + public unsafe readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLVertexDescriptor vertexDescriptor) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor { @@ -72,6 +76,22 @@ namespace Ryujinx.Graphics.Metal { renderCommandEncoder.SetDepthStencilState(_depthStencilState.Value); } + + if (_viewports.Length > 0) + { + fixed (MTLViewport* pMtlViewports = _viewports) + { + renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_viewports.Length); + } + } + + if (_scissors.Length > 0) + { + fixed (MTLScissorRect* pMtlScissors = _scissors) + { + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_scissors.Length); + } + } } public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace) @@ -107,5 +127,15 @@ namespace Ryujinx.Graphics.Metal return state; } + + public void UpdateScissors(MTLScissorRect[] scissors) + { + _scissors = scissors; + } + + public void UpdateViewport(MTLViewport[] viewports) + { + _viewports = viewports; + } } } -- 2.47.1 From 9d7164a32963fead84a24578744106b3fb91d615 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 21 Mar 2024 11:45:50 -0400 Subject: [PATCH 099/368] Remove TODOs --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 327bd849f..14c6b99a6 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -528,7 +528,6 @@ namespace Ryujinx.Graphics.Metal if (maxScissors == 0) { return; } - // TODO: Test max allowed scissor rects on device var mtlScissorRects = new MTLScissorRect[maxScissors]; for (int i = 0; i < maxScissors; i++) @@ -721,7 +720,6 @@ namespace Ryujinx.Graphics.Metal public unsafe void SetViewports(ReadOnlySpan viewports) { - // TODO: Test max allowed viewports on device var mtlViewports = new MTLViewport[viewports.Length]; for (int i = 0; i < viewports.Length; i++) -- 2.47.1 From b85721b738f0dd86d56d573af9fb39664052a358 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 22 Apr 2024 17:44:55 -0400 Subject: [PATCH 100/368] Rebase + GAL Changes --- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 2 ++ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 12 ++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 10 ++++++++++ src/Ryujinx.Graphics.Metal/Texture.cs | 13 +++++++------ src/Ryujinx/AppHost.cs | 2 +- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index d0987f0fe..428e90caa 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -1,9 +1,11 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + [SupportedOSPlatform("macos")] static class EnumConversion { public static MTLSamplerAddressMode Convert(this AddressMode mode) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index d1f321c2e..aabe0c1e6 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -70,6 +70,11 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } + public IImageArray CreateImageArray(int size, bool isBuffer) + { + throw new NotImplementedException(); + } + public BufferHandle CreateBuffer(int size, BufferAccess access) { var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); @@ -100,6 +105,11 @@ namespace Ryujinx.Graphics.Metal return texture; } + public ITextureArray CreateTextureArray(int size, bool isBuffer) + { + throw new NotImplementedException(); + } + public bool PrepareHostMapping(IntPtr address, ulong size) { // TODO: Metal Host Mapping @@ -157,6 +167,8 @@ namespace Ryujinx.Graphics.Metal supportsCubemapView: true, supportsNonConstantTextureOffset: false, supportsScaledVertexFormats: true, + // TODO: Metal Bindless Support + supportsSeparateSampler: false, supportsShaderBallot: false, supportsShaderBarrierDivergence: false, supportsShaderFloat64: false, diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 14c6b99a6..6c33699e3 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -428,6 +428,11 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } + public void SetImageArray(ShaderStage stage, int binding, IImageArray array) + { + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + } + public void SetLineParameters(float width, bool smooth) { // Not supported in Metal @@ -644,6 +649,11 @@ namespace Ryujinx.Graphics.Metal } } + public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) + { + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + } + public void SetUniformBuffers(ReadOnlySpan buffers) { _uniformBuffers = []; diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 8cf62aea3..fb1c92be5 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -3,6 +3,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System; +using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -170,11 +171,11 @@ namespace Ryujinx.Graphics.Metal } // TODO: Handle array formats - public unsafe void SetData(SpanOrArray data) + public unsafe void SetData(IMemoryOwner data) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); - var dataSpan = data.Span; + var dataSpan = data.Memory.Span; var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); dataSpan.CopyTo(bufferSpan); @@ -222,7 +223,7 @@ namespace Ryujinx.Graphics.Metal } } - public void SetData(SpanOrArray data, int layer, int level) + public void SetData(IMemoryOwner data, int layer, int level) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); @@ -235,7 +236,7 @@ namespace Ryujinx.Graphics.Metal unsafe { - var dataSpan = data.Span; + var dataSpan = data.Memory.Span; var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); dataSpan.CopyTo(bufferSpan); @@ -254,7 +255,7 @@ namespace Ryujinx.Graphics.Metal } } - public void SetData(SpanOrArray data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); @@ -267,7 +268,7 @@ namespace Ryujinx.Graphics.Metal unsafe { - var dataSpan = data.Span; + var dataSpan = data.Memory.Span; var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); dataSpan.CopyTo(bufferSpan); diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 110b73e7e..cbf073caa 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1127,6 +1127,7 @@ namespace Ryujinx.Ava { GraphicsBackend.Vulkan => "Vulkan", GraphicsBackend.OpenGl => "OpenGL", + GraphicsBackend.Metal => "Metal", _ => throw new NotImplementedException() }; @@ -1149,7 +1150,6 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( vSyncMode, LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", - ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToString(), dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), $"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", -- 2.47.1 From ba05ed9552154cfba7f42302ad786d1c686d2869 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 22 Apr 2024 17:51:31 -0400 Subject: [PATCH 101/368] Format --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 6c33699e3..94d0bc19e 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -531,7 +531,10 @@ namespace Ryujinx.Graphics.Metal { int maxScissors = Math.Min(regions.Length, _renderEncoderState.ViewportCount); - if (maxScissors == 0) { return; } + if (maxScissors == 0) + { + return; + } var mtlScissorRects = new MTLScissorRect[maxScissors]; @@ -549,7 +552,8 @@ namespace Ryujinx.Graphics.Metal } _renderEncoderState.UpdateScissors(mtlScissorRects); - if (_currentEncoderType == EncoderType.Render) { + if (_currentEncoderType == EncoderType.Render) + { fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) { var renderCommandEncoder = GetOrCreateRenderEncoder(); -- 2.47.1 From 8a0dd491b92897959839ff2761cdfe186681f0de Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 16:36:01 +0200 Subject: [PATCH 102/368] support texture views --- src/Ryujinx.Graphics.Metal/Texture.cs | 60 ++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index fb1c92be5..bc5beb302 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; +using SharpMetal.Foundation; using SharpMetal.Metal; using System; using System.Buffers; @@ -81,6 +82,62 @@ namespace Ryujinx.Graphics.Metal MTLTexture = _device.NewTexture(descriptor); } + public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) { + _device = device; + _pipeline = pipeline; + _info = info; + + var pixelFormat = FormatTable.GetFormat(Info.Format); + var textureType = Info.Target.Convert(); + NSRange levels; + levels.location = (ulong)firstLevel; + levels.length = (ulong)Info.Levels; + NSRange slices; + slices.location = (ulong)firstLayer; + slices.length = 1; + + if (info.Target == Target.Texture3D) + { + slices.length = (ulong)Info.Depth; + } + else if (info.Target != Target.Cubemap) + { + slices.length = (ulong)Info.Depth; + } + + var swizzleR = Info.SwizzleR.Convert(); + var swizzleG = Info.SwizzleG.Convert(); + var swizzleB = Info.SwizzleB.Convert(); + var swizzleA = Info.SwizzleA.Convert(); + + if (info.Format == Format.R5G5B5A1Unorm || + info.Format == Format.R5G5B5X1Unorm || + info.Format == Format.R5G6B5Unorm) + { + (swizzleB, swizzleR) = (swizzleR, swizzleB); + } + else if (pixelFormat == MTLPixelFormat.ABGR4Unorm || info.Format == Format.A1B5G5R5Unorm) + { + var tempB = swizzleB; + var tempA = swizzleA; + + swizzleB = swizzleG; + swizzleA = swizzleR; + swizzleR = tempA; + swizzleG = tempB; + } + + var swizzle = new MTLTextureSwizzleChannels + { + red = swizzleR, + green = swizzleG, + blue = swizzleB, + alpha = swizzleA + }; + + MTLTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + } + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); @@ -156,8 +213,7 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - throw new NotImplementedException(); + return new Texture(_device, _pipeline, info, MTLTexture, firstLayer, firstLevel); } public PinnedSpan GetData() -- 2.47.1 From e3364b0fcc5bdf74220c811084836c3726cce7ff Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 17:01:11 +0200 Subject: [PATCH 103/368] support fragment coord as an input to a shader --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 6 ++++++ .../CodeGen/Msl/Instructions/IoMap.cs | 1 + 2 files changed, 7 insertions(+) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 2e87da675..4f6015ee7 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -133,6 +133,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.EnterScope(); + if (context.Definitions.Stage == ShaderStage.Fragment) + { + // TODO: check if it's needed + context.AppendLine("float4 position [[position]];"); + } + foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 2ec7a1779..9ead6bc56 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -30,6 +30,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), IoVariable.VertexId => ("vertex_id", AggregateType.S32), IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), + IoVariable.FragmentCoord => ("in.position", AggregateType.Vector4 | AggregateType.FP32), _ => (null, AggregateType.Invalid), }; } -- 2.47.1 From b2a0ca0e2bf92425eedee55479384056ad6d322a Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 17:41:16 +0200 Subject: [PATCH 104/368] add: textures and samplers as shader arguments & fix: issue with casting --- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 8 ++++---- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 8 ++++---- .../CodeGen/Msl/Instructions/InstType.cs | 1 - src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 7 +++++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 7991c942e..2ee3495e6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -53,13 +53,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:", 12); Add(Instruction.ConvertFP32ToFP64, 0); // MSL does not have a 64-bit FP Add(Instruction.ConvertFP64ToFP32, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertFP32ToS32, InstType.Cast, "int"); - Add(Instruction.ConvertFP32ToU32, InstType.Cast, "uint"); + Add(Instruction.ConvertFP32ToS32, InstType.CallUnary, "int"); + Add(Instruction.ConvertFP32ToU32, InstType.CallUnary, "uint"); Add(Instruction.ConvertFP64ToS32, 0); // MSL does not have a 64-bit FP Add(Instruction.ConvertFP64ToU32, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertS32ToFP32, InstType.Cast, "float"); + Add(Instruction.ConvertS32ToFP32, InstType.CallUnary, "float"); Add(Instruction.ConvertS32ToFP64, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertU32ToFP32, InstType.Cast, "float"); + Add(Instruction.ConvertU32ToFP32, InstType.CallUnary, "float"); Add(Instruction.ConvertU32ToFP64, 0); // MSL does not have a 64-bit FP Add(Instruction.Cosine, InstType.CallUnary, "cos"); Add(Instruction.Ddx, InstType.CallUnary, "dfdx"); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 2cec9d18a..6639445e7 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -158,7 +158,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions bool colorIsVector = isGather || !isShadow; - string texCall = "texture."; + string samplerName = GetSamplerName(context.Properties, texOp); + string texCall = $"tex_{samplerName}"; + texCall += "."; int srcIndex = 0; @@ -175,9 +177,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { texCall += "sample("; - string samplerName = GetSamplerName(context.Properties, texOp); - - texCall += samplerName; + texCall += $"samp_{samplerName}"; } int coordsCount = texOp.Type.GetDimensions(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs index 85930cb24..d8f6bfed1 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs @@ -29,7 +29,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Call = 1 << 10, Atomic = 1 << 11, Special = 1 << 12, - Cast = 1 << 13, ArityMask = 0xff, } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 8fa9df0ca..1fe7f46ef 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -122,6 +122,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { args = args.Append($"device float4 *{storageBuffers.Name} [[buffer({storageBuffers.Binding})]]").ToArray(); } + + foreach (var texture in context.Properties.Textures.Values) + { + // TODO: don't use always texture2d + args = args.Append($"texture2d tex_{texture.Name} [[texture({texture.Binding})]]").ToArray(); + args = args.Append($"sampler samp_{texture.Name} [[sampler({texture.Binding})]]").ToArray(); + } } return $"{funcKeyword} {returnType} {funcName ?? function.Name}({string.Join(", ", args)})"; -- 2.47.1 From de23abcf90bf56dbc971944c269ff53f6bc75901 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 17:57:42 +0200 Subject: [PATCH 105/368] create GetSwizzle helper function --- src/Ryujinx.Graphics.Metal/Texture.cs | 40 ++++++--------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index bc5beb302..d8e3f9b68 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -49,35 +49,7 @@ namespace Ryujinx.Graphics.Metal descriptor.ArrayLength = (ulong)Info.Depth; } - var swizzleR = Info.SwizzleR.Convert(); - var swizzleG = Info.SwizzleG.Convert(); - var swizzleB = Info.SwizzleB.Convert(); - var swizzleA = Info.SwizzleA.Convert(); - - if (info.Format == Format.R5G5B5A1Unorm || - info.Format == Format.R5G5B5X1Unorm || - info.Format == Format.R5G6B5Unorm) - { - (swizzleB, swizzleR) = (swizzleR, swizzleB); - } - else if (descriptor.PixelFormat == MTLPixelFormat.ABGR4Unorm || info.Format == Format.A1B5G5R5Unorm) - { - var tempB = swizzleB; - var tempA = swizzleA; - - swizzleB = swizzleG; - swizzleA = swizzleR; - swizzleR = tempA; - swizzleG = tempB; - } - - descriptor.Swizzle = new MTLTextureSwizzleChannels - { - red = swizzleR, - green = swizzleG, - blue = swizzleB, - alpha = swizzleA - }; + descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat); MTLTexture = _device.NewTexture(descriptor); } @@ -105,6 +77,12 @@ namespace Ryujinx.Graphics.Metal slices.length = (ulong)Info.Depth; } + var swizzle = GetSwizzle(info, pixelFormat); + + MTLTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + } + + private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) { var swizzleR = Info.SwizzleR.Convert(); var swizzleG = Info.SwizzleG.Convert(); var swizzleB = Info.SwizzleB.Convert(); @@ -127,15 +105,13 @@ namespace Ryujinx.Graphics.Metal swizzleG = tempB; } - var swizzle = new MTLTextureSwizzleChannels + return new MTLTextureSwizzleChannels { red = swizzleR, green = swizzleG, blue = swizzleB, alpha = swizzleA }; - - MTLTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) -- 2.47.1 From d66c39b64b0896eae5b99caffab43b28cdd97ab6 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 20:51:53 +0200 Subject: [PATCH 106/368] don't hardcode render pipeline attachments --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 ++-- src/Ryujinx.Graphics.Metal/Program.cs | 2 ++ .../RenderEncoderState.cs | 24 ++++++++++++------- src/Ryujinx.Graphics.Metal/Sampler.cs | 3 ++- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 94d0bc19e..dd2e3bf99 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -137,7 +137,7 @@ namespace Ryujinx.Graphics.Metal } var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); - _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); + _renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor); RebindBuffers(renderCommandEncoder); @@ -193,7 +193,7 @@ namespace Ryujinx.Graphics.Metal _helperShaders.BlitShader.VertexFunction, _helperShaders.BlitShader.FragmentFunction, _device); - _renderEncoderState.SetEncoderState(renderCommandEncoder, _vertexDescriptor); + _renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor); var sampler = _device.NewSamplerState(new MTLSamplerDescriptor { diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 764bcf126..255a7316b 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Metal { Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); _status = ProgramLinkStatus.Failure; + //Console.WriteLine($"SHADER {index}: {shader.Code}"); + //throw new NotImplementedException(); return; } diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 4a045c96b..832e0ba36 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Metal _device = device; } - public unsafe readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLVertexDescriptor vertexDescriptor) + public unsafe readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLRenderPassDescriptor descriptor, MTLVertexDescriptor vertexDescriptor) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor { @@ -53,13 +53,21 @@ namespace Ryujinx.Graphics.Metal renderPipelineDescriptor.FragmentFunction = _fragmentFunction.Value; } - var attachment = renderPipelineDescriptor.ColorAttachments.Object(0); - attachment.SetBlendingEnabled(true); - attachment.PixelFormat = MTLPixelFormat.BGRA8Unorm; - attachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - attachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - attachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - attachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + const int maxColorAttachments = 8; + for (int i = 0; i < maxColorAttachments; i++) + { + var renderAttachment = descriptor.ColorAttachments.Object((ulong)i); + if (renderAttachment.Texture != null) + { + var attachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); + attachment.SetBlendingEnabled(true); + attachment.PixelFormat = renderAttachment.Texture.PixelFormat; + attachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + attachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + attachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + attachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + } + } var error = new NSError(IntPtr.Zero); var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index f4ffecc02..00570b8a9 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System.Runtime.Versioning; +using System; namespace Ryujinx.Graphics.Metal { @@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Metal LodMinClamp = info.MinLod, LodMaxClamp = info.MaxLod, LodAverage = false, - MaxAnisotropy = (uint)info.MaxAnisotropy, + MaxAnisotropy = Math.Max((uint)info.MaxAnisotropy, 1), SAddressMode = info.AddressU.Convert(), TAddressMode = info.AddressV.Convert(), RAddressMode = info.AddressP.Convert() -- 2.47.1 From 362dc6eaeabe667b654c06522a50ebd45d6384a4 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 20:55:26 +0200 Subject: [PATCH 107/368] use unknown texture usage --- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index d8e3f9b68..6dca2b2a3 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLTextureDescriptor { PixelFormat = FormatTable.GetFormat(Info.Format), - Usage = MTLTextureUsage.ShaderRead, + Usage = MTLTextureUsage.Unknown, SampleCount = (ulong)Info.Samples, TextureType = Info.Target.Convert(), Width = (ulong)Info.Width, -- 2.47.1 From 18e156994148564708623d30fc6c0377409761cf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 15 May 2024 09:03:53 -0400 Subject: [PATCH 108/368] Rebase + Format --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 3 ++- src/Ryujinx.Graphics.Metal/RenderEncoderState.cs | 12 ++++++------ src/Ryujinx.Graphics.Metal/Sampler.cs | 2 +- src/Ryujinx.Graphics.Metal/Texture.cs | 6 ++++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index aabe0c1e6..7342a363b 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -152,6 +152,7 @@ namespace Ryujinx.Graphics.Metal supportsBgraFormat: true, supportsR4G4Format: false, supportsR4G4B4A4Format: true, + supportsScaledVertexFormats: true, supportsSnormBufferTextureFormat: true, supportsSparseBuffer: false, supports5BitComponentFormat: true, @@ -166,7 +167,7 @@ namespace Ryujinx.Graphics.Metal supportsMismatchingViewFormat: true, supportsCubemapView: true, supportsNonConstantTextureOffset: false, - supportsScaledVertexFormats: true, + supportsQuads: false, // TODO: Metal Bindless Support supportsSeparateSampler: false, supportsShaderBallot: false, diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 832e0ba36..9ee4ee642 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -18,8 +18,8 @@ namespace Ryujinx.Graphics.Metal private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always; private bool _depthWriteEnabled = false; - private MTLStencilDescriptor _backFaceStencil = new MTLStencilDescriptor(); - private MTLStencilDescriptor _frontFaceStencil = new MTLStencilDescriptor(); + private MTLStencilDescriptor _backFaceStencil = new(); + private MTLStencilDescriptor _frontFaceStencil = new(); public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; @@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Metal private MTLViewport[] _viewports = []; private MTLScissorRect[] _scissors = []; - public int ViewportCount => _viewports.Length; + public readonly int ViewportCount => _viewports.Length; public RenderEncoderState(MTLFunction vertexFunction, MTLFunction fragmentFunction, MTLDevice device) { @@ -53,11 +53,11 @@ namespace Ryujinx.Graphics.Metal renderPipelineDescriptor.FragmentFunction = _fragmentFunction.Value; } - const int maxColorAttachments = 8; - for (int i = 0; i < maxColorAttachments; i++) + const int MaxColorAttachments = 8; + for (int i = 0; i < MaxColorAttachments; i++) { var renderAttachment = descriptor.ColorAttachments.Object((ulong)i); - if (renderAttachment.Texture != null) + if (renderAttachment.Texture != IntPtr.Zero) { var attachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); attachment.SetBlendingEnabled(true); diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index 00570b8a9..0c556eac4 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -1,7 +1,7 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; -using System.Runtime.Versioning; using System; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 6dca2b2a3..abfadb53a 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -54,7 +54,8 @@ namespace Ryujinx.Graphics.Metal MTLTexture = _device.NewTexture(descriptor); } - public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) { + public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) + { _device = device; _pipeline = pipeline; _info = info; @@ -82,7 +83,8 @@ namespace Ryujinx.Graphics.Metal MTLTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); } - private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) { + private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) + { var swizzleR = Info.SwizzleR.Convert(); var swizzleG = Info.SwizzleG.Convert(); var swizzleB = Info.SwizzleB.Convert(); -- 2.47.1 From c3a9a0d62559933ed2c03ef734de80a09a2d74d9 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 15:49:29 +0200 Subject: [PATCH 109/368] determine type of buffer by its field types --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 1fe7f46ef..0d0d7470d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -115,12 +115,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (var constantBuffer in context.Properties.ConstantBuffers.Values) { - args = args.Append($"constant float4 *{constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); + var varType = constantBuffer.Type.Fields[0].Type & ~AggregateType.Array; + args = args.Append($"constant {Declarations.GetVarTypeName(context, varType)} *{constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); } foreach (var storageBuffers in context.Properties.StorageBuffers.Values) { - args = args.Append($"device float4 *{storageBuffers.Name} [[buffer({storageBuffers.Binding})]]").ToArray(); + var varType = storageBuffers.Type.Fields[0].Type & ~AggregateType.Array; + args = args.Append($"device {Declarations.GetVarTypeName(context, varType)} *{storageBuffers.Name} [[buffer({storageBuffers.Binding})]]").ToArray(); } foreach (var texture in context.Properties.Textures.Values) -- 2.47.1 From 6095f14646e9ee6cdf48e6cce5f9ce72dbf306a9 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 15:55:03 +0200 Subject: [PATCH 110/368] add: vertex and instance id arguments --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 0d0d7470d..745723f6d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -113,6 +113,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } + // TODO: add these only if they are used + if (stage == ShaderStage.Vertex) + { + args = args.Append("uint vertex_id [[vertex_id]]").ToArray(); + args = args.Append("uint instance_id [[instance_id]]").ToArray(); + } + foreach (var constantBuffer in context.Properties.ConstantBuffers.Values) { var varType = constantBuffer.Type.Fields[0].Type & ~AggregateType.Array; -- 2.47.1 From 72eb47513c8bd25cbfc8b6aa3914cbcea1b6a9d4 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:01:57 +0200 Subject: [PATCH 111/368] fix: incorrect abs instruction --- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 2ee3495e6..af6e8058a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.AtomicOr, InstType.AtomicBinary, "atomic_or_explicit"); Add(Instruction.AtomicSwap, InstType.AtomicBinary, "atomic_exchange_explicit"); Add(Instruction.AtomicXor, InstType.AtomicBinary, "atomic_xor_explicit"); - Add(Instruction.Absolute, InstType.AtomicBinary, "atomic_abs_explicit"); + Add(Instruction.Absolute, InstType.CallUnary, "abs"); Add(Instruction.Add, InstType.OpBinaryCom, "+", 2); Add(Instruction.Ballot, InstType.CallUnary, "simd_ballot"); Add(Instruction.Barrier, InstType.Special); -- 2.47.1 From 2bef29b2004f6ab6a31fc190a66f4ef08877a1a6 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:06:35 +0200 Subject: [PATCH 112/368] offset storage buffer bindings by 15 --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 3 ++- src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index dd2e3bf99..33d5678a4 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -600,11 +600,12 @@ namespace Ryujinx.Graphics.Metal { if (buffer.Range.Size != 0) { + // Offset the binding by 15 _storageBuffers.Add(new BufferInfo { Handle = buffer.Range.Handle.ToIntPtr(), Offset = buffer.Range.Offset, - Index = buffer.Binding + Index = buffer.Binding + 15 }); } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 745723f6d..3b515eb84 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -129,7 +129,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (var storageBuffers in context.Properties.StorageBuffers.Values) { var varType = storageBuffers.Type.Fields[0].Type & ~AggregateType.Array; - args = args.Append($"device {Declarations.GetVarTypeName(context, varType)} *{storageBuffers.Name} [[buffer({storageBuffers.Binding})]]").ToArray(); + // Offset the binding by 15 to avoid clashing with the constant buffers + args = args.Append($"device {Declarations.GetVarTypeName(context, varType)} *{storageBuffers.Name} [[buffer({15 + storageBuffers.Binding})]]").ToArray(); } foreach (var texture in context.Properties.Textures.Values) -- 2.47.1 From a66ab905a98ebac470a69cce33a6edfef2b75179 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:20:52 +0200 Subject: [PATCH 113/368] don't hardcode texture type --- .../CodeGen/Msl/MslGenerator.cs | 4 +-- src/Ryujinx.Graphics.Shader/SamplerType.cs | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 3b515eb84..5852deca7 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -135,8 +135,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (var texture in context.Properties.Textures.Values) { - // TODO: don't use always texture2d - args = args.Append($"texture2d tex_{texture.Name} [[texture({texture.Binding})]]").ToArray(); + var textureTypeName = texture.Type.ToMslTextureType(); + args = args.Append($"{textureTypeName} tex_{texture.Name} [[texture({texture.Binding})]]").ToArray(); args = args.Append($"sampler samp_{texture.Name} [[sampler({texture.Binding})]]").ToArray(); } } diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index a693495fa..f9ae96661 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -155,5 +155,31 @@ namespace Ryujinx.Graphics.Shader return typeName; } + + public static string ToMslTextureType(this SamplerType type) + { + string typeName = (type & SamplerType.Mask) switch + { + SamplerType.None => "texture", + SamplerType.Texture1D => "texture1d", + SamplerType.TextureBuffer => "texturebuffer", + SamplerType.Texture2D => "texture2d", + SamplerType.Texture3D => "texture3d", + SamplerType.TextureCube => "texturecube", + _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), + }; + + if ((type & SamplerType.Multisample) != 0) + { + typeName += "_ms"; + } + + if ((type & SamplerType.Array) != 0) + { + typeName += "_array"; + } + + return typeName + ""; + } } } -- 2.47.1 From b7414c1e4db6916ad735559b13f7cb0723ea8cb6 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:24:13 +0200 Subject: [PATCH 114/368] don't declare samplers for separate textures --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 5852deca7..eea78a9ee 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -137,7 +137,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { var textureTypeName = texture.Type.ToMslTextureType(); args = args.Append($"{textureTypeName} tex_{texture.Name} [[texture({texture.Binding})]]").ToArray(); - args = args.Append($"sampler samp_{texture.Name} [[sampler({texture.Binding})]]").ToArray(); + // If the texture is not separate, we need to declare a sampler + if (!texture.Separate) + { + args = args.Append($"sampler samp_{texture.Name} [[sampler({texture.Binding})]]").ToArray(); + } } } -- 2.47.1 From d08218a809213c88427d32d7c49b59175dcba212 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:33:46 +0200 Subject: [PATCH 115/368] fix: pass array index as an additional argument to sample --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 6639445e7..217c21816 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -184,13 +184,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions int pCount = coordsCount; - int arrayIndexElem = -1; - - if (isArray) - { - arrayIndexElem = pCount++; - } - if (isShadow && !isGather) { pCount++; @@ -211,19 +204,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions for (int index = 0; index < count; index++) { - if (arrayIndexElem == index) - { - elems[index] = Src(AggregateType.S32); - - if (!intCoords) - { - elems[index] = "float(" + elems[index] + ")"; - } - } - else - { - elems[index] = Src(coordType); - } + elems[index] = Src(coordType); } string prefix = intCoords ? "int" : "float"; @@ -238,6 +219,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Append(AssemblePVector(pCount)); + if (isArray) + { + texCall += ", " + Src(AggregateType.S32); + } + texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); return texCall; -- 2.47.1 From 22d3fa068d396cb13f471e10e044652b2390888b Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:42:19 +0200 Subject: [PATCH 116/368] use 0 instead of undef --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs index 0ec14bfef..8a468395e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs @@ -10,6 +10,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string ArgumentNamePrefix = "a"; - public const string UndefinedName = "undef"; + public const string UndefinedName = "0"; } } -- 2.47.1 From 91d1bb6c08880a17478a4884b0a2a5c856958940 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:54:54 +0200 Subject: [PATCH 117/368] format --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index eea78a9ee..18953943e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { var varType = storageBuffers.Type.Fields[0].Type & ~AggregateType.Array; // Offset the binding by 15 to avoid clashing with the constant buffers - args = args.Append($"device {Declarations.GetVarTypeName(context, varType)} *{storageBuffers.Name} [[buffer({15 + storageBuffers.Binding})]]").ToArray(); + args = args.Append($"device {Declarations.GetVarTypeName(context, varType)} *{storageBuffers.Name} [[buffer({storageBuffers.Binding + 15})]]").ToArray(); } foreach (var texture in context.Properties.Textures.Values) -- 2.47.1 From 824321c88aef060b95a1044774b505f8b0c6d531 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 16 May 2024 15:59:56 -0400 Subject: [PATCH 118/368] Clamp Viewport ZNear & ZFar --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 33d5678a4..961512c1f 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -735,6 +735,11 @@ namespace Ryujinx.Graphics.Metal public unsafe void SetViewports(ReadOnlySpan viewports) { + static float Clamp(float value) + { + return Math.Clamp(value, 0f, 1f); + } + var mtlViewports = new MTLViewport[viewports.Length]; for (int i = 0; i < viewports.Length; i++) @@ -746,8 +751,8 @@ namespace Ryujinx.Graphics.Metal originY = viewport.Region.Y, width = viewport.Region.Width, height = viewport.Region.Height, - znear = viewport.DepthNear, - zfar = viewport.DepthFar + znear = Clamp(viewport.DepthNear), + zfar = Clamp(viewport.DepthFar) }; } -- 2.47.1 From 7f65ec0b8ccb5e349ae85338362a1c8c6b89cd72 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 16 May 2024 20:29:37 -0400 Subject: [PATCH 119/368] Set Depth Attachment Texture --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 961512c1f..3fbfc07bf 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -30,6 +30,7 @@ namespace Ryujinx.Graphics.Metal private MTLCommandEncoder? _currentEncoder; private EncoderType _currentEncoderType = EncoderType.None; private MTLTexture[] _renderTargets = []; + private MTLTexture _depthTarget; private RenderEncoderState _renderEncoderState; private readonly MTLVertexDescriptor _vertexDescriptor = new(); @@ -136,6 +137,10 @@ namespace Ryujinx.Graphics.Metal } } + var depthAttachment = descriptor.DepthAttachment; + depthAttachment.Texture = _depthTarget; + depthAttachment.LoadAction = MTLLoadAction.Load; + var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor); @@ -523,6 +528,11 @@ namespace Ryujinx.Graphics.Metal } } + if (depthStencil is Texture depthTexture) + { + _depthTarget = depthTexture.MTLTexture; + } + // Recreate Render Command Encoder BeginRenderPass(); } -- 2.47.1 From f79ebd1141797f9ba59fc4b48c622b2bc7daf4b9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 16 May 2024 20:54:27 -0400 Subject: [PATCH 120/368] Set DepthAttachmentPixelFormat --- src/Ryujinx.Graphics.Metal/RenderEncoderState.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 9ee4ee642..2ce5cbad1 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -69,6 +69,8 @@ namespace Ryujinx.Graphics.Metal } } + renderPipelineDescriptor.DepthAttachmentPixelFormat = descriptor.DepthAttachment.Texture.PixelFormat; + var error = new NSError(IntPtr.Zero); var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); if (error != IntPtr.Zero) -- 2.47.1 From 96d884a15b3fa97d79481f42120cb6c9195e8552 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 16 May 2024 21:06:04 -0400 Subject: [PATCH 121/368] Clamp ScissorRect --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 6 +++--- src/Ryujinx.Graphics.Metal/RenderEncoderState.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 3fbfc07bf..fbb3e31b6 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -539,7 +539,7 @@ namespace Ryujinx.Graphics.Metal public unsafe void SetScissors(ReadOnlySpan> regions) { - int maxScissors = Math.Min(regions.Length, _renderEncoderState.ViewportCount); + int maxScissors = Math.Min(regions.Length, _renderEncoderState.Viewports.Length); if (maxScissors == 0) { @@ -554,8 +554,8 @@ namespace Ryujinx.Graphics.Metal mtlScissorRects[i] = new MTLScissorRect { - height = (ulong)region.Height, - width = (ulong)region.Width, + height = Math.Clamp((ulong)region.Height, 0, (ulong)_renderEncoderState.Viewports[i].height), + width = Math.Clamp((ulong)region.Width, 0, (ulong)_renderEncoderState.Viewports[i].width), x = (ulong)region.X, y = (ulong)region.Y }; diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index 2ce5cbad1..c22f98522 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Metal private MTLViewport[] _viewports = []; private MTLScissorRect[] _scissors = []; - public readonly int ViewportCount => _viewports.Length; + public readonly MTLViewport[] Viewports => _viewports; public RenderEncoderState(MTLFunction vertexFunction, MTLFunction fragmentFunction, MTLDevice device) { @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Metal _device = device; } - public unsafe readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLRenderPassDescriptor descriptor, MTLVertexDescriptor vertexDescriptor) + public unsafe void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLRenderPassDescriptor descriptor, MTLVertexDescriptor vertexDescriptor) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor { -- 2.47.1 From c7b6e4cf80087d0b2e7b8dc1caa6f632e5bfdfe7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 18:54:55 -0400 Subject: [PATCH 122/368] Break everything :D --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 50 ++ .../EncoderStateManager.cs | 558 ++++++++++++++++++ src/Ryujinx.Graphics.Metal/HelperShader.cs | 206 +++++++ src/Ryujinx.Graphics.Metal/HelperShaders.cs | 44 -- src/Ryujinx.Graphics.Metal/Pipeline.cs | 400 ++----------- src/Ryujinx.Graphics.Metal/Program.cs | 2 - .../RenderEncoderState.cs | 151 ----- .../Ryujinx.Graphics.Metal.csproj | 6 +- src/Ryujinx.Graphics.Metal/Sampler.cs | 5 + .../Blit.metal} | 4 +- .../Shaders/ColorClearF.metal | 0 .../Shaders/ColorClearSI.metal | 0 .../Shaders/ColorClearUI.metal | 0 .../Shaders/DepthStencilClear.metal | 0 src/Ryujinx.Graphics.Metal/Texture.cs | 1 - 15 files changed, 885 insertions(+), 542 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/EncoderState.cs create mode 100644 src/Ryujinx.Graphics.Metal/EncoderStateManager.cs create mode 100644 src/Ryujinx.Graphics.Metal/HelperShader.cs delete mode 100644 src/Ryujinx.Graphics.Metal/HelperShaders.cs delete mode 100644 src/Ryujinx.Graphics.Metal/RenderEncoderState.cs rename src/Ryujinx.Graphics.Metal/{HelperShadersSource.metal => Shaders/Blit.metal} (85%) create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs new file mode 100644 index 000000000..e4be1fb7c --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -0,0 +1,50 @@ +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System.Collections.Generic; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public struct EncoderState + { + public MTLFunction? VertexFunction = null; + public MTLFunction? FragmentFunction = null; + + public Dictionary FragmentTextures = new(); + public Dictionary FragmentSamplers = new(); + + public Dictionary VertexTextures = new(); + public Dictionary VertexSamplers = new(); + + public List VertexBuffers = []; + public List UniformBuffers = []; + public List StorageBuffers = []; + + public MTLBuffer IndexBuffer = default; + public MTLIndexType IndexType = MTLIndexType.UInt16; + public ulong IndexBufferOffset = 0; + + public MTLDepthStencilState? DepthStencilState = null; + + public MTLCompareFunction DepthCompareFunction = MTLCompareFunction.Always; + public bool DepthWriteEnabled = false; + + public MTLStencilDescriptor BackFaceStencil = new(); + public MTLStencilDescriptor FrontFaceStencil = new(); + + public PrimitiveTopology Topology = PrimitiveTopology.Triangles; + public MTLCullMode CullMode = MTLCullMode.None; + public MTLWinding Winding = MTLWinding.Clockwise; + + public MTLViewport[] Viewports = []; + public MTLScissorRect[] Scissors = []; + + // Changes to attachments take recreation! + public MTLTexture DepthStencil = default; + public MTLTexture[] RenderTargets = []; + public MTLVertexDescriptor VertexDescriptor = new(); + + public EncoderState() { } + } +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs new file mode 100644 index 000000000..a7e7de281 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -0,0 +1,558 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + struct EncoderStateManager + { + private readonly MTLDevice _device; + private Pipeline _pipeline; + + private EncoderState _currentState = new(); + private EncoderState _backState = new(); + + // Public accessors + public MTLBuffer IndexBuffer => _currentState.IndexBuffer; + public MTLIndexType IndexType => _currentState.IndexType; + public ulong IndexBufferOffset => _currentState.IndexBufferOffset; + public PrimitiveTopology Topology => _currentState.Topology; + + public EncoderStateManager(MTLDevice device, Pipeline pipeline) + { + _device = device; + _pipeline = pipeline; + } + + public void SwapStates() + { + (_currentState, _backState) = (_backState, _currentState); + + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + + public MTLRenderCommandEncoder CreateRenderCommandEncoder() + { + // Initialise Pass & State + + var renderPassDescriptor = new MTLRenderPassDescriptor(); + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + + const int MaxColorAttachments = 8; + for (int i = 0; i < MaxColorAttachments; i++) + { + if (_currentState.RenderTargets[i] != IntPtr.Zero) + { + var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); + passAttachment.Texture = _currentState.RenderTargets[i]; + passAttachment.LoadAction = MTLLoadAction.Load; + + var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); + pipelineAttachment.SetBlendingEnabled(true); + pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; + pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + } + } + + var depthAttachment = renderPassDescriptor.DepthAttachment; + depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.LoadAction = MTLLoadAction.Load; + + // var stencilAttachment = renderPassDescriptor.StencilAttachment; + // stencilAttachment.Texture = + // stencilAttachment.LoadAction = MTLLoadAction.Load; + + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + // renderPipelineDescriptor.StencilAttachmentPixelFormat = + + renderPipelineDescriptor.VertexDescriptor = _currentState.VertexDescriptor; + + if (_currentState.VertexFunction != null) + { + renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; + } + else + { + return new (IntPtr.Zero); + } + + if (_currentState.FragmentFunction != null) + { + renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; + } + + var error = new NSError(IntPtr.Zero); + var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + // Initialise Encoder + + var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); + + renderCommandEncoder.SetRenderPipelineState(pipelineState); + + SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); + SetScissors(renderCommandEncoder, _currentState.Scissors); + SetViewports(renderCommandEncoder, _currentState.Viewports); + SetBuffers(renderCommandEncoder, _currentState.VertexBuffers); + SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + SetCullMode(renderCommandEncoder, _currentState.CullMode); + SetFrontFace(renderCommandEncoder, _currentState.Winding); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + + return renderCommandEncoder; + } + + public void UpdateIndexBuffer(BufferRange buffer, IndexType type) + { + if (buffer.Handle != BufferHandle.Null) + { + _currentState.IndexType = type.Convert(); + _currentState.IndexBufferOffset = (ulong)buffer.Offset; + var handle = buffer.Handle; + _currentState.IndexBuffer = new(Unsafe.As(ref handle)); + } + } + + public void UpdatePrimitiveTopology(PrimitiveTopology topology) + { + _currentState.Topology = topology; + } + + public void UpdateProgram(IProgram program) + { + Program prg = (Program)program; + + if (prg.VertexFunction == IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!"); + return; + } + + _currentState.VertexFunction = prg.VertexFunction; + _currentState.FragmentFunction = prg.FragmentFunction; + + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + + public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) + { + _currentState.RenderTargets = new MTLTexture[colors.Length]; + + for (int i = 0; i < colors.Length; i++) + { + if (colors[i] is not Texture tex) + { + continue; + } + + _currentState.RenderTargets[i] = tex.MTLTexture; + } + + if (depthStencil is Texture depthTexture) + { + _currentState.DepthStencil = depthTexture.MTLTexture; + } + + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + + public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) + { + for (int i = 0; i < vertexAttribs.Length; i++) + { + if (!vertexAttribs[i].IsZero) + { + // TODO: Format should not be hardcoded + var attrib = _currentState.VertexDescriptor.Attributes.Object((ulong)i); + attrib.Format = MTLVertexFormat.Float4; + attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; + attrib.Offset = (ulong)vertexAttribs[i].Offset; + + var layout = _currentState.VertexDescriptor.Layouts.Object((ulong)vertexAttribs[i].BufferIndex); + layout.Stride = 1; + } + } + + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + + // Inlineable + public void UpdateStencilState(StencilTestDescriptor stencilTest) + { + _currentState.BackFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = stencilTest.BackSFail.Convert(), + DepthFailureOperation = stencilTest.BackDpFail.Convert(), + DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), + StencilCompareFunction = stencilTest.BackFunc.Convert(), + ReadMask = (uint)stencilTest.BackFuncMask, + WriteMask = (uint)stencilTest.BackMask + }; + + _currentState.FrontFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = stencilTest.FrontSFail.Convert(), + DepthFailureOperation = stencilTest.FrontDpFail.Convert(), + DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), + StencilCompareFunction = stencilTest.FrontFunc.Convert(), + ReadMask = (uint)stencilTest.FrontFuncMask, + WriteMask = (uint)stencilTest.FrontMask + }; + + _currentState.DepthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + { + DepthCompareFunction = _currentState.DepthCompareFunction, + DepthWriteEnabled = _currentState.DepthWriteEnabled, + BackFaceStencil = stencilTest.TestEnable ? _currentState.BackFaceStencil : new MTLStencilDescriptor(IntPtr.Zero), + FrontFaceStencil = stencilTest.TestEnable ? _currentState.FrontFaceStencil : new MTLStencilDescriptor(IntPtr.Zero) + }); + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); + } + } + + // Inlineable + public void UpdateDepthState(DepthTestDescriptor depthTest) + { + _currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; + _currentState.DepthWriteEnabled = depthTest.WriteEnable; + + _currentState.DepthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + { + DepthCompareFunction = _currentState.DepthCompareFunction, + DepthWriteEnabled = _currentState.DepthWriteEnabled, + BackFaceStencil = _currentState.BackFaceStencil, + FrontFaceStencil = _currentState.FrontFaceStencil + }); + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); + } + } + + // Inlineable + public void UpdateScissors(ReadOnlySpan> regions) + { + int maxScissors = Math.Min(regions.Length, _currentState.Viewports.Length); + + if (maxScissors == 0) + { + return; + } + + _currentState.Scissors = new MTLScissorRect[maxScissors]; + + for (int i = 0; i < maxScissors; i++) + { + var region = regions[i]; + + _currentState.Scissors[i] = new MTLScissorRect + { + height = Math.Clamp((ulong)region.Height, 0, (ulong)_currentState.Viewports[i].height), + width = Math.Clamp((ulong)region.Width, 0, (ulong)_currentState.Viewports[i].width), + x = (ulong)region.X, + y = (ulong)region.Y + }; + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetScissors(renderCommandEncoder, _currentState.Scissors); + } + } + + // Inlineable + public void UpdateViewports(ReadOnlySpan viewports) + { + static float Clamp(float value) + { + return Math.Clamp(value, 0f, 1f); + } + + _currentState.Viewports = new MTLViewport[viewports.Length]; + + for (int i = 0; i < viewports.Length; i++) + { + var viewport = viewports[i]; + _currentState.Viewports[i] = new MTLViewport + { + originX = viewport.Region.X, + originY = viewport.Region.Y, + width = viewport.Region.Width, + height = viewport.Region.Height, + znear = Clamp(viewport.DepthNear), + zfar = Clamp(viewport.DepthFar) + }; + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetViewports(renderCommandEncoder, _currentState.Viewports); + } + } + + // Inlineable + public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) + { + _currentState.VertexBuffers = []; + + for (int i = 0; i < vertexBuffers.Length; i++) + { + if (vertexBuffers[i].Stride != 0) + { + var layout = _currentState.VertexDescriptor.Layouts.Object((ulong)i); + layout.Stride = (ulong)vertexBuffers[i].Stride; + + _currentState.VertexBuffers.Add(new BufferInfo + { + Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(), + Offset = vertexBuffers[i].Buffer.Offset, + Index = i + }); + } + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetBuffers(renderCommandEncoder, _currentState.VertexBuffers); + } + } + + // Inlineable + public void UpdateUniformBuffers(ReadOnlySpan buffers) + { + _currentState.UniformBuffers = []; + + foreach (BufferAssignment buffer in buffers) + { + if (buffer.Range.Size != 0) + { + _currentState.UniformBuffers.Add(new BufferInfo + { + Handle = buffer.Range.Handle.ToIntPtr(), + Offset = buffer.Range.Offset, + Index = buffer.Binding + }); + } + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + } + } + + // Inlineable + public void UpdateStorageBuffers(ReadOnlySpan buffers) + { + _currentState.StorageBuffers = []; + + foreach (BufferAssignment buffer in buffers) + { + if (buffer.Range.Size != 0) + { + // TODO: DONT offset the binding by 15 + _currentState.StorageBuffers.Add(new BufferInfo + { + Handle = buffer.Range.Handle.ToIntPtr(), + Offset = buffer.Range.Offset, + Index = buffer.Binding + 15 + }); + } + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + } + } + + // Inlineable + public void UpdateCullMode(bool enable, Face face) + { + _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetCullMode(renderCommandEncoder, _currentState.CullMode); + } + } + + // Inlineable + public void UpdateFrontFace(FrontFace frontFace) + { + _currentState.Winding = frontFace.Convert(); + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetFrontFace(renderCommandEncoder, _currentState.Winding); + } + } + + // Inlineable + public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, MTLTexture texture, MTLSamplerState sampler) + { + switch (stage) + { + case ShaderStage.Fragment: + _currentState.FragmentTextures[binding] = texture; + _currentState.FragmentSamplers[binding] = sampler; + break; + case ShaderStage.Vertex: + _currentState.VertexTextures[binding] = texture; + _currentState.VertexSamplers[binding] = sampler; + break; + } + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + } + } + + private static void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder, MTLDepthStencilState? depthStencilState) + { + if (depthStencilState != null) + { + renderCommandEncoder.SetDepthStencilState(depthStencilState.Value); + } + } + + private unsafe static void SetScissors(MTLRenderCommandEncoder renderCommandEncoder, MTLScissorRect[] scissors) + { + if (scissors.Length > 0) + { + fixed (MTLScissorRect* pMtlScissors = scissors) + { + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)scissors.Length); + } + } + } + + private unsafe static void SetViewports(MTLRenderCommandEncoder renderCommandEncoder, MTLViewport[] viewports) + { + if (viewports.Length > 0) + { + fixed (MTLViewport* pMtlViewports = viewports) + { + renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); + } + } + } + + private static void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) + { + foreach (var buffer in buffers) + { + renderCommandEncoder.SetVertexBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index); + + if (fragment) + { + renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index); + } + } + } + + private static void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder, MTLCullMode cullMode) + { + renderCommandEncoder.SetCullMode(cullMode); + } + + private static void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder, MTLWinding winding) + { + renderCommandEncoder.SetFrontFacingWinding(winding); + } + + private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary textures, Dictionary samplers) + { + foreach (var texture in textures) + { + switch (stage) + { + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexTexture(texture.Value, texture.Key); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentTexture(texture.Value, texture.Key); + break; + } + } + + foreach (var sampler in samplers) + { + switch (stage) + { + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexSamplerState(sampler.Value, sampler.Key); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentSamplerState(sampler.Value, sampler.Key); + break; + } + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs new file mode 100644 index 000000000..a700cd68a --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -0,0 +1,206 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + enum ComponentType + { + Float, + SignedInteger, + UnsignedInteger, + } + + [SupportedOSPlatform("macos")] + class HelperShader : IDisposable + { + private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders"; + private MTLDevice _device; + private Pipeline _pipeline; + + private readonly IProgram _programColorBlit; + private readonly IProgram _programColorClearF; + private readonly IProgram _programColorClearSI; + private readonly IProgram _programColorClearUI; + private readonly IProgram _programDepthStencilClear; + + public HelperShader(MTLDevice device, Pipeline pipeline) + { + _device = device; + _pipeline = pipeline; + + var blitSource = ReadMsl("Blit.metal"); + _programColorBlit = new Program( + [ + new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], device); + + // var colorClearFSource = ReadMsl("ColorClearF.metal"); + // _programColorClearF = new Program( + // [ + // new ShaderSource(colorClearFSource, ShaderStage.Fragment, TargetLanguage.Msl), + // new ShaderSource(colorClearFSource, ShaderStage.Vertex, TargetLanguage.Msl) + // ], device); + // + // var colorClearSiSource = ReadMsl("ColorClearSI.metal"); + // _programColorClearSI = new Program( + // [ + // new ShaderSource(colorClearSiSource, ShaderStage.Fragment, TargetLanguage.Msl), + // new ShaderSource(colorClearSiSource, ShaderStage.Vertex, TargetLanguage.Msl) + // ], device); + // + // var colorClearUiSource = ReadMsl("ColorClearUI.metal"); + // _programColorClearUI = new Program( + // [ + // new ShaderSource(colorClearUiSource, ShaderStage.Fragment, TargetLanguage.Msl), + // new ShaderSource(colorClearUiSource, ShaderStage.Vertex, TargetLanguage.Msl) + // ], device); + // + // var depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); + // _programDepthStencilClear = new Program( + // [ + // new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), + // new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) + // ], device); + } + + private static string ReadMsl(string fileName) + { + return EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName)); + } + + public void BlitColor( + ITexture source, + ITexture destination) + { + var sampler = _device.NewSamplerState(new MTLSamplerDescriptor + { + MinFilter = MTLSamplerMinMagFilter.Nearest, + MagFilter = MTLSamplerMinMagFilter.Nearest, + MipFilter = MTLSamplerMipFilter.NotMipmapped + }); + + _pipeline.SetProgram(_programColorBlit); + _pipeline.SetRenderTargets([destination], null); + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, source, new Sampler(sampler)); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.Triangles); + _pipeline.Draw(6, 1, 0, 0); + _pipeline.Finish(); + } + + public void ClearColor( + Texture dst, + uint componentMask, + int dstWidth, + int dstHeight, + ComponentType type, + Rectangle scissor) + { + Span viewports = stackalloc Viewport[1]; + + viewports[0] = new Viewport( + new Rectangle(0, 0, dstWidth, dstHeight), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + IProgram program; + + if (type == ComponentType.SignedInteger) + { + program = _programColorClearSI; + } + else if (type == ComponentType.UnsignedInteger) + { + program = _programColorClearUI; + } + else + { + program = _programColorClearF; + } + + _pipeline.SetProgram(program); + // _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight); + _pipeline.SetRenderTargetColorMasks([componentMask]); + _pipeline.SetViewports(viewports); + _pipeline.SetScissors([scissor]); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + _pipeline.Draw(4, 1, 0, 0); + _pipeline.Finish(); + } + + public void ClearDepthStencil( + Texture dst, + float depthValue, + bool depthMask, + int stencilValue, + int stencilMask, + int dstWidth, + int dstHeight, + Rectangle scissor) + { + Span viewports = stackalloc Viewport[1]; + + viewports[0] = new Viewport( + new Rectangle(0, 0, dstWidth, dstHeight), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + _pipeline.SetProgram(_programDepthStencilClear); + // _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight); + _pipeline.SetViewports(viewports); + _pipeline.SetScissors([scissor]); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); + _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); + _pipeline.Draw(4, 1, 0, 0); + _pipeline.Finish(); + } + + private static StencilTestDescriptor CreateStencilTestDescriptor( + bool enabled, + int refValue = 0, + int compareMask = 0xff, + int writeMask = 0xff) + { + return new StencilTestDescriptor( + enabled, + CompareOp.Always, + StencilOp.Replace, + StencilOp.Replace, + StencilOp.Replace, + refValue, + compareMask, + writeMask, + CompareOp.Always, + StencilOp.Replace, + StencilOp.Replace, + StencilOp.Replace, + refValue, + compareMask, + writeMask); + } + + public void Dispose() + { + _programColorBlit.Dispose(); + _programColorClearF.Dispose(); + _programColorClearSI.Dispose(); + _programColorClearUI.Dispose(); + _programDepthStencilClear.Dispose(); + _pipeline.Dispose(); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/HelperShaders.cs b/src/Ryujinx.Graphics.Metal/HelperShaders.cs deleted file mode 100644 index 8ca7adb6f..000000000 --- a/src/Ryujinx.Graphics.Metal/HelperShaders.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - public class HelperShaders - { - private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/HelperShadersSource.metal"; - - public HelperShader BlitShader; - - public HelperShaders(MTLDevice device) - { - var error = new NSError(IntPtr.Zero); - - var shaderSource = EmbeddedResources.ReadAllText(ShadersSourcePath); - var library = device.NewLibrary(StringHelper.NSString(shaderSource), new(IntPtr.Zero), ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Library: {StringHelper.String(error.LocalizedDescription)}"); - } - - BlitShader = new HelperShader(library, "vertexBlit", "fragmentBlit"); - } - } - - [SupportedOSPlatform("macos")] - public readonly struct HelperShader - { - public readonly MTLFunction VertexFunction; - public readonly MTLFunction FragmentFunction; - - public HelperShader(MTLLibrary library, string vertex, string fragment) - { - VertexFunction = library.NewFunction(StringHelper.NSString(vertex)); - FragmentFunction = library.NewFunction(StringHelper.NSString(fragment)); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index fbb3e31b6..1b29a8562 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -5,7 +5,6 @@ using SharpMetal.Foundation; using SharpMetal.Metal; using SharpMetal.QuartzCore; using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -24,37 +23,27 @@ namespace Ryujinx.Graphics.Metal { private readonly MTLDevice _device; private readonly MTLCommandQueue _commandQueue; - private readonly HelperShaders _helperShaders; + private readonly HelperShader _helperShader; private MTLCommandBuffer _commandBuffer; + public MTLCommandBuffer CommandBuffer => _commandBuffer; + private MTLCommandEncoder? _currentEncoder; + public MTLCommandEncoder? CurrentEncoder => _currentEncoder; + private EncoderType _currentEncoderType = EncoderType.None; - private MTLTexture[] _renderTargets = []; - private MTLTexture _depthTarget; + public EncoderType CurrentEncoderType => _currentEncoderType; - private RenderEncoderState _renderEncoderState; - private readonly MTLVertexDescriptor _vertexDescriptor = new(); - private List _vertexBuffers = []; - private List _uniformBuffers = []; - private List _storageBuffers = []; - - private MTLBuffer _indexBuffer; - private MTLIndexType _indexType; - private ulong _indexBufferOffset; - private MTLClearColor _clearColor; + private EncoderStateManager _encoderStateManager; public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { _device = device; _commandQueue = commandQueue; - _helperShaders = new HelperShaders(_device); - - _renderEncoderState = new RenderEncoderState( - _helperShaders.BlitShader.VertexFunction, - _helperShaders.BlitShader.FragmentFunction, - _device); + _helperShader = new HelperShader(_device, this); _commandBuffer = _commandQueue.CommandBuffer(); + _encoderStateManager = new EncoderStateManager(_device, this); } public MTLRenderCommandEncoder GetOrCreateRenderEncoder() @@ -126,28 +115,11 @@ namespace Ryujinx.Graphics.Metal { EndCurrentPass(); - var descriptor = new MTLRenderPassDescriptor(); - for (int i = 0; i < _renderTargets.Length; i++) - { - if (_renderTargets[i] != null) - { - var attachment = descriptor.ColorAttachments.Object((ulong)i); - attachment.Texture = _renderTargets[i]; - attachment.LoadAction = MTLLoadAction.Load; - } - } - - var depthAttachment = descriptor.DepthAttachment; - depthAttachment.Texture = _depthTarget; - depthAttachment.LoadAction = MTLLoadAction.Load; - - var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); - _renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor); - - RebindBuffers(renderCommandEncoder); + var renderCommandEncoder = _encoderStateManager.CreateRenderCommandEncoder(); _currentEncoder = renderCommandEncoder; _currentEncoderType = EncoderType.Render; + return renderCommandEncoder; } @@ -184,34 +156,9 @@ namespace Ryujinx.Graphics.Metal EndCurrentPass(); - var descriptor = new MTLRenderPassDescriptor(); - var colorAttachment = descriptor.ColorAttachments.Object(0); + _encoderStateManager.SwapStates(); - colorAttachment.Texture = drawable.Texture; - colorAttachment.LoadAction = MTLLoadAction.Clear; - colorAttachment.ClearColor = _clearColor; - - descriptor.ColorAttachments.SetObject(colorAttachment, 0); - - var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); - _renderEncoderState = new RenderEncoderState( - _helperShaders.BlitShader.VertexFunction, - _helperShaders.BlitShader.FragmentFunction, - _device); - _renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor); - - var sampler = _device.NewSamplerState(new MTLSamplerDescriptor - { - MinFilter = MTLSamplerMinMagFilter.Nearest, - MagFilter = MTLSamplerMinMagFilter.Nearest, - MipFilter = MTLSamplerMipFilter.NotMipmapped - }); - - renderCommandEncoder.SetFragmentTexture(tex.MTLTexture, 0); - renderCommandEncoder.SetFragmentSamplerState(sampler, 0); - - renderCommandEncoder.DrawPrimitives(MTLPrimitiveType.Triangle, 0, 6); - renderCommandEncoder.EndEncoding(); + // _helperShader.BlitColor(tex, drawable.Texture); _commandBuffer.PresentDrawable(drawable); _commandBuffer.Commit(); @@ -219,31 +166,16 @@ namespace Ryujinx.Graphics.Metal _commandBuffer = _commandQueue.CommandBuffer(); } + public void Finish() + { + _encoderStateManager.SwapStates(); + } + public void Barrier() { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public void RebindBuffers(MTLRenderCommandEncoder renderCommandEncoder) - { - foreach (var vertexBuffer in _vertexBuffers) - { - renderCommandEncoder.SetVertexBuffer(new MTLBuffer(vertexBuffer.Handle), (ulong)vertexBuffer.Offset, (ulong)vertexBuffer.Index); - } - - foreach (var uniformBuffer in _uniformBuffers) - { - renderCommandEncoder.SetVertexBuffer(new MTLBuffer(uniformBuffer.Handle), (ulong)uniformBuffer.Offset, (ulong)uniformBuffer.Index); - renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(uniformBuffer.Handle), (ulong)uniformBuffer.Offset, (ulong)uniformBuffer.Index); - } - - foreach (var storageBuffer in _storageBuffers) - { - renderCommandEncoder.SetVertexBuffer(new MTLBuffer(storageBuffer.Handle), (ulong)storageBuffer.Offset, (ulong)storageBuffer.Index); - renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(storageBuffer.Handle), (ulong)storageBuffer.Offset, (ulong)storageBuffer.Index); - } - } - public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) { var blitCommandEncoder = GetOrCreateBlitEncoder(); @@ -262,11 +194,10 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - _clearColor = new MTLClearColor { red = color.Red, green = color.Green, blue = color.Blue, alpha = color.Alpha }; + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, - int stencilMask) + public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } @@ -301,9 +232,14 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = GetOrCreateRenderEncoder(); // TODO: Support topology re-indexing to provide support for TriangleFans - var primitiveType = _renderEncoderState.Topology.Convert(); + var primitiveType = _encoderStateManager.Topology.Convert(); - renderCommandEncoder.DrawPrimitives(primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance); + renderCommandEncoder.DrawPrimitives( + primitiveType, + (ulong)firstVertex, + (ulong)vertexCount, + (ulong)instanceCount, + (ulong)firstInstance); } public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) @@ -311,9 +247,17 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = GetOrCreateRenderEncoder(); // TODO: Support topology re-indexing to provide support for TriangleFans - var primitiveType = _renderEncoderState.Topology.Convert(); + var primitiveType = _encoderStateManager.Topology.Convert(); - renderCommandEncoder.DrawIndexedPrimitives(primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance); + renderCommandEncoder.DrawIndexedPrimitives( + primitiveType, + (ulong)indexCount, + _encoderStateManager.IndexType, + _encoderStateManager.IndexBuffer, + _encoderStateManager.IndexBufferOffset, + (ulong)instanceCount, + firstVertex, + (ulong)firstInstance); } public void DrawIndexedIndirect(BufferRange indirectBuffer) @@ -383,49 +327,22 @@ namespace Ryujinx.Graphics.Metal public void SetDepthTest(DepthTestDescriptor depthTest) { - var depthStencilState = _renderEncoderState.UpdateDepthState( - depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always, - depthTest.WriteEnable); - - if (_currentEncoderType == EncoderType.Render) - { - new MTLRenderCommandEncoder(_currentEncoder.Value).SetDepthStencilState(depthStencilState); - } + _encoderStateManager.UpdateDepthState(depthTest); } public void SetFaceCulling(bool enable, Face face) { - var cullMode = enable ? face.Convert() : MTLCullMode.None; - - if (_currentEncoderType == EncoderType.Render) - { - new MTLRenderCommandEncoder(_currentEncoder.Value).SetCullMode(cullMode); - } - - _renderEncoderState.CullMode = cullMode; + _encoderStateManager.UpdateCullMode(enable, face); } public void SetFrontFace(FrontFace frontFace) { - var winding = frontFace.Convert(); - - if (_currentEncoderType == EncoderType.Render) - { - new MTLRenderCommandEncoder(_currentEncoder.Value).SetFrontFacingWinding(winding); - } - - _renderEncoderState.Winding = winding; + _encoderStateManager.UpdateFrontFace(frontFace); } public void SetIndexBuffer(BufferRange buffer, IndexType type) { - if (buffer.Handle != BufferHandle.Null) - { - _indexType = type.Convert(); - _indexBufferOffset = (ulong)buffer.Offset; - var handle = buffer.Handle; - _indexBuffer = new(Unsafe.As(ref handle)); - } + _encoderStateManager.UpdateIndexBuffer(buffer, type); } public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) @@ -482,23 +399,12 @@ namespace Ryujinx.Graphics.Metal public void SetPrimitiveTopology(PrimitiveTopology topology) { - _renderEncoderState.Topology = topology; + _encoderStateManager.UpdatePrimitiveTopology(topology); } public void SetProgram(IProgram program) { - Program prg = (Program)program; - - if (prg.VertexFunction == IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!"); - return; - } - - _renderEncoderState = new RenderEncoderState( - prg.VertexFunction, - prg.FragmentFunction, - _device); + _encoderStateManager.UpdateProgram(program); } public void SetRasterizerDiscard(bool discard) @@ -513,118 +419,27 @@ namespace Ryujinx.Graphics.Metal public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) { - _renderTargets = new MTLTexture[colors.Length]; - - for (int i = 0; i < colors.Length; i++) - { - if (colors[i] is not Texture tex) - { - continue; - } - - if (tex.MTLTexture != null) - { - _renderTargets[i] = tex.MTLTexture; - } - } - - if (depthStencil is Texture depthTexture) - { - _depthTarget = depthTexture.MTLTexture; - } - - // Recreate Render Command Encoder - BeginRenderPass(); + _encoderStateManager.UpdateRenderTargets(colors, depthStencil); } - public unsafe void SetScissors(ReadOnlySpan> regions) + public void SetScissors(ReadOnlySpan> regions) { - int maxScissors = Math.Min(regions.Length, _renderEncoderState.Viewports.Length); - - if (maxScissors == 0) - { - return; - } - - var mtlScissorRects = new MTLScissorRect[maxScissors]; - - for (int i = 0; i < maxScissors; i++) - { - var region = regions[i]; - - mtlScissorRects[i] = new MTLScissorRect - { - height = Math.Clamp((ulong)region.Height, 0, (ulong)_renderEncoderState.Viewports[i].height), - width = Math.Clamp((ulong)region.Width, 0, (ulong)_renderEncoderState.Viewports[i].width), - x = (ulong)region.X, - y = (ulong)region.Y - }; - } - - _renderEncoderState.UpdateScissors(mtlScissorRects); - if (_currentEncoderType == EncoderType.Render) - { - fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissorRects, (ulong)regions.Length); - } - } + _encoderStateManager.UpdateScissors(regions); } public void SetStencilTest(StencilTestDescriptor stencilTest) { - var backFace = new MTLStencilDescriptor - { - StencilFailureOperation = stencilTest.BackSFail.Convert(), - DepthFailureOperation = stencilTest.BackDpFail.Convert(), - DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), - StencilCompareFunction = stencilTest.BackFunc.Convert(), - ReadMask = (uint)stencilTest.BackFuncMask, - WriteMask = (uint)stencilTest.BackMask - }; + _encoderStateManager.UpdateStencilState(stencilTest); + } - var frontFace = new MTLStencilDescriptor - { - StencilFailureOperation = stencilTest.FrontSFail.Convert(), - DepthFailureOperation = stencilTest.FrontDpFail.Convert(), - DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), - StencilCompareFunction = stencilTest.FrontFunc.Convert(), - ReadMask = (uint)stencilTest.FrontFuncMask, - WriteMask = (uint)stencilTest.FrontMask - }; - - var depthStencilState = _renderEncoderState.UpdateStencilState(backFace, frontFace); - - if (_currentEncoderType == EncoderType.Render) - { - new MTLRenderCommandEncoder(_currentEncoder.Value).SetDepthStencilState(depthStencilState); - } + public void SetUniformBuffers(ReadOnlySpan buffers) + { + _encoderStateManager.UpdateUniformBuffers(buffers); } public void SetStorageBuffers(ReadOnlySpan buffers) { - _storageBuffers = []; - - foreach (BufferAssignment buffer in buffers) - { - if (buffer.Range.Size != 0) - { - // Offset the binding by 15 - _storageBuffers.Add(new BufferInfo - { - Handle = buffer.Range.Handle.ToIntPtr(), - Offset = buffer.Range.Offset, - Index = buffer.Binding + 15 - }); - } - } - - if (_currentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - RebindBuffers(renderCommandEncoder); - } + _encoderStateManager.UpdateStorageBuffers(buffers); } public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) @@ -633,27 +448,18 @@ namespace Ryujinx.Graphics.Metal { if (sampler is Sampler samp) { - MTLRenderCommandEncoder renderCommandEncoder; - MTLComputeCommandEncoder computeCommandEncoder; - var mtlTexture = tex.MTLTexture; var mtlSampler = samp.GetSampler(); var index = (ulong)binding; switch (stage) { - case ShaderStage.Fragment: - renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetFragmentTexture(mtlTexture, index); - renderCommandEncoder.SetFragmentSamplerState(mtlSampler, index); - break; case ShaderStage.Vertex: - renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetVertexTexture(mtlTexture, index); - renderCommandEncoder.SetVertexSamplerState(mtlSampler, index); + case ShaderStage.Fragment: + _encoderStateManager.UpdateTextureAndSampler(stage, index, mtlTexture, mtlSampler); break; case ShaderStage.Compute: - computeCommandEncoder = GetOrCreateComputeEncoder(); + var computeCommandEncoder = GetOrCreateComputeEncoder(); computeCommandEncoder.SetTexture(mtlTexture, index); computeCommandEncoder.SetSamplerState(mtlSampler, index); break; @@ -669,30 +475,6 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public void SetUniformBuffers(ReadOnlySpan buffers) - { - _uniformBuffers = []; - - foreach (BufferAssignment buffer in buffers) - { - if (buffer.Range.Size != 0) - { - _uniformBuffers.Add(new BufferInfo - { - Handle = buffer.Range.Handle.ToIntPtr(), - Offset = buffer.Range.Offset, - Index = buffer.Binding - }); - } - } - - if (_currentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - RebindBuffers(renderCommandEncoder); - } - } - public void SetUserClipDistance(int index, bool enableClip) { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); @@ -700,81 +482,17 @@ namespace Ryujinx.Graphics.Metal public void SetVertexAttribs(ReadOnlySpan vertexAttribs) { - for (int i = 0; i < vertexAttribs.Length; i++) - { - if (!vertexAttribs[i].IsZero) - { - // TODO: Format should not be hardcoded - var attrib = _vertexDescriptor.Attributes.Object((ulong)i); - attrib.Format = MTLVertexFormat.Float4; - attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; - attrib.Offset = (ulong)vertexAttribs[i].Offset; - - var layout = _vertexDescriptor.Layouts.Object((ulong)vertexAttribs[i].BufferIndex); - layout.Stride = 1; - } - } + _encoderStateManager.UpdateVertexAttribs(vertexAttribs); } public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - _vertexBuffers = []; - - for (int i = 0; i < vertexBuffers.Length; i++) - { - if (vertexBuffers[i].Stride != 0) - { - var layout = _vertexDescriptor.Layouts.Object((ulong)i); - layout.Stride = (ulong)vertexBuffers[i].Stride; - - _vertexBuffers.Add(new BufferInfo - { - Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(), - Offset = vertexBuffers[i].Buffer.Offset, - Index = i - }); - } - } - - if (_currentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - RebindBuffers(renderCommandEncoder); - } + _encoderStateManager.UpdateVertexBuffers(vertexBuffers); } - public unsafe void SetViewports(ReadOnlySpan viewports) + public void SetViewports(ReadOnlySpan viewports) { - static float Clamp(float value) - { - return Math.Clamp(value, 0f, 1f); - } - - var mtlViewports = new MTLViewport[viewports.Length]; - - for (int i = 0; i < viewports.Length; i++) - { - var viewport = viewports[i]; - mtlViewports[i] = new MTLViewport - { - originX = viewport.Region.X, - originY = viewport.Region.Y, - width = viewport.Region.Width, - height = viewport.Region.Height, - znear = Clamp(viewport.DepthNear), - zfar = Clamp(viewport.DepthFar) - }; - } - - _renderEncoderState.UpdateViewport(mtlViewports); - if (_currentEncoderType == EncoderType.Render) - { - fixed (MTLViewport* pMtlViewports = mtlViewports) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); - } - } + _encoderStateManager.UpdateViewports(viewports); } public void TextureBarrier() diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 255a7316b..764bcf126 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -28,8 +28,6 @@ namespace Ryujinx.Graphics.Metal { Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); _status = ProgramLinkStatus.Failure; - //Console.WriteLine($"SHADER {index}: {shader.Code}"); - //throw new NotImplementedException(); return; } diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs deleted file mode 100644 index c22f98522..000000000 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ /dev/null @@ -1,151 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - struct RenderEncoderState - { - private readonly MTLDevice _device; - private readonly MTLFunction? _vertexFunction = null; - private readonly MTLFunction? _fragmentFunction = null; - private MTLDepthStencilState? _depthStencilState = null; - - private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always; - private bool _depthWriteEnabled = false; - - private MTLStencilDescriptor _backFaceStencil = new(); - private MTLStencilDescriptor _frontFaceStencil = new(); - - public PrimitiveTopology Topology = PrimitiveTopology.Triangles; - public MTLCullMode CullMode = MTLCullMode.None; - public MTLWinding Winding = MTLWinding.Clockwise; - - private MTLViewport[] _viewports = []; - private MTLScissorRect[] _scissors = []; - public readonly MTLViewport[] Viewports => _viewports; - - public RenderEncoderState(MTLFunction vertexFunction, MTLFunction fragmentFunction, MTLDevice device) - { - _vertexFunction = vertexFunction; - _fragmentFunction = fragmentFunction; - _device = device; - } - - public unsafe void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLRenderPassDescriptor descriptor, MTLVertexDescriptor vertexDescriptor) - { - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor - { - VertexDescriptor = vertexDescriptor - }; - - if (_vertexFunction != null) - { - renderPipelineDescriptor.VertexFunction = _vertexFunction.Value; - } - - if (_fragmentFunction != null) - { - renderPipelineDescriptor.FragmentFunction = _fragmentFunction.Value; - } - - const int MaxColorAttachments = 8; - for (int i = 0; i < MaxColorAttachments; i++) - { - var renderAttachment = descriptor.ColorAttachments.Object((ulong)i); - if (renderAttachment.Texture != IntPtr.Zero) - { - var attachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - attachment.SetBlendingEnabled(true); - attachment.PixelFormat = renderAttachment.Texture.PixelFormat; - attachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - attachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - attachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - attachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - } - } - - renderPipelineDescriptor.DepthAttachmentPixelFormat = descriptor.DepthAttachment.Texture.PixelFormat; - - var error = new NSError(IntPtr.Zero); - var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - renderCommandEncoder.SetRenderPipelineState(pipelineState); - renderCommandEncoder.SetCullMode(CullMode); - renderCommandEncoder.SetFrontFacingWinding(Winding); - - if (_depthStencilState != null) - { - renderCommandEncoder.SetDepthStencilState(_depthStencilState.Value); - } - - if (_viewports.Length > 0) - { - fixed (MTLViewport* pMtlViewports = _viewports) - { - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_viewports.Length); - } - } - - if (_scissors.Length > 0) - { - fixed (MTLScissorRect* pMtlScissors = _scissors) - { - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_scissors.Length); - } - } - } - - public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace) - { - _backFaceStencil = backFace; - _frontFaceStencil = frontFace; - - _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor - { - DepthCompareFunction = _depthCompareFunction, - DepthWriteEnabled = _depthWriteEnabled, - BackFaceStencil = _backFaceStencil, - FrontFaceStencil = _frontFaceStencil - }); - - return _depthStencilState.Value; - } - - public MTLDepthStencilState UpdateDepthState(MTLCompareFunction depthCompareFunction, bool depthWriteEnabled) - { - _depthCompareFunction = depthCompareFunction; - _depthWriteEnabled = depthWriteEnabled; - - var state = _device.NewDepthStencilState(new MTLDepthStencilDescriptor - { - DepthCompareFunction = _depthCompareFunction, - DepthWriteEnabled = _depthWriteEnabled, - BackFaceStencil = _backFaceStencil, - FrontFaceStencil = _frontFaceStencil - }); - - _depthStencilState = state; - - return state; - } - - public void UpdateScissors(MTLScissorRect[] scissors) - { - _scissors = scissors; - } - - public void UpdateViewport(MTLViewport[] viewports) - { - _viewports = viewports; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 46f2d070d..d8da12834 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -15,7 +15,11 @@ - + + + + + diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index 0c556eac4..5cb898a9f 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -33,6 +33,11 @@ namespace Ryujinx.Graphics.Metal _mtlSamplerState = samplerState; } + public Sampler(MTLSamplerState samplerState) + { + _mtlSamplerState = samplerState; + } + public MTLSamplerState GetSampler() { return _mtlSamplerState; diff --git a/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal similarity index 85% rename from src/Ryujinx.Graphics.Metal/HelperShadersSource.metal rename to src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index dd39c6a89..b2bec3e8e 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -20,7 +20,7 @@ struct CopyVertexOut { float2 uv; }; -vertex CopyVertexOut vertexBlit(unsigned short vid [[vertex_id]]) { +vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) { float2 position = quadVertices[vid]; CopyVertexOut out; @@ -32,7 +32,7 @@ vertex CopyVertexOut vertexBlit(unsigned short vid [[vertex_id]]) { return out; } -fragment float4 fragmentBlit(CopyVertexOut in [[stage_in]], +fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], texture2d texture [[texture(0)]], sampler sampler [[sampler(0)]]) { return texture.sample(sampler, in.uv); diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal new file mode 100644 index 000000000..e69de29bb diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal new file mode 100644 index 000000000..e69de29bb diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal new file mode 100644 index 000000000..e69de29bb diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal new file mode 100644 index 000000000..e69de29bb diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index abfadb53a..5ce1dd2ed 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Foundation; using SharpMetal.Metal; -- 2.47.1 From 4190abbbf54d84b9c88fe932311a5092ecc917ea Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 19:59:38 -0400 Subject: [PATCH 123/368] Fix Depth/Stencil attachments --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 1 + .../EncoderStateManager.cs | 78 ++++++++++++++----- src/Ryujinx.Graphics.Metal/FormatTable.cs | 16 +++- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index e4be1fb7c..ed512125b 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Metal public MTLStencilDescriptor BackFaceStencil = new(); public MTLStencilDescriptor FrontFaceStencil = new(); + public bool StencilTestEnabled = false; public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index a7e7de281..49c3b1ee8 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -68,15 +68,43 @@ namespace Ryujinx.Graphics.Metal } var depthAttachment = renderPassDescriptor.DepthAttachment; - depthAttachment.Texture = _currentState.DepthStencil; - depthAttachment.LoadAction = MTLLoadAction.Load; + var stencilAttachment = renderPassDescriptor.StencilAttachment; - // var stencilAttachment = renderPassDescriptor.StencilAttachment; - // stencilAttachment.Texture = - // stencilAttachment.LoadAction = MTLLoadAction.Load; + switch (_currentState.DepthStencil.PixelFormat) + { + // Depth Only Attachment + case MTLPixelFormat.Depth16Unorm: + case MTLPixelFormat.Depth32Float: + depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.LoadAction = MTLLoadAction.Load; + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - // renderPipelineDescriptor.StencilAttachmentPixelFormat = + // Stencil Only Attachment + case MTLPixelFormat.Stencil8: + stencilAttachment.Texture = _currentState.DepthStencil; + stencilAttachment.LoadAction = MTLLoadAction.Load; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; + + // Combined Attachment + case MTLPixelFormat.Depth24UnormStencil8: + case MTLPixelFormat.Depth32FloatStencil8: + depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.LoadAction = MTLLoadAction.Load; + + var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.PixelFormat); + var stencilView = _currentState.DepthStencil.NewTextureView(unpackedFormat); + stencilAttachment.Texture = stencilView; + stencilAttachment.LoadAction = MTLLoadAction.Load; + + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; + default: + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); + break; + } renderPipelineDescriptor.VertexDescriptor = _currentState.VertexDescriptor; @@ -86,7 +114,7 @@ namespace Ryujinx.Graphics.Metal } else { - return new (IntPtr.Zero); + return new(IntPtr.Zero); } if (_currentState.FragmentFunction != null) @@ -230,13 +258,21 @@ namespace Ryujinx.Graphics.Metal WriteMask = (uint)stencilTest.FrontMask }; - _currentState.DepthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + _currentState.StencilTestEnabled = stencilTest.TestEnable; + + var descriptor = new MTLDepthStencilDescriptor { DepthCompareFunction = _currentState.DepthCompareFunction, - DepthWriteEnabled = _currentState.DepthWriteEnabled, - BackFaceStencil = stencilTest.TestEnable ? _currentState.BackFaceStencil : new MTLStencilDescriptor(IntPtr.Zero), - FrontFaceStencil = stencilTest.TestEnable ? _currentState.FrontFaceStencil : new MTLStencilDescriptor(IntPtr.Zero) - }); + DepthWriteEnabled = _currentState.DepthWriteEnabled + }; + + if (_currentState.StencilTestEnabled) + { + descriptor.BackFaceStencil = _currentState.BackFaceStencil; + descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; + } + + _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); // Inline Update @@ -253,13 +289,19 @@ namespace Ryujinx.Graphics.Metal _currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; _currentState.DepthWriteEnabled = depthTest.WriteEnable; - _currentState.DepthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + var descriptor = new MTLDepthStencilDescriptor { DepthCompareFunction = _currentState.DepthCompareFunction, - DepthWriteEnabled = _currentState.DepthWriteEnabled, - BackFaceStencil = _currentState.BackFaceStencil, - FrontFaceStencil = _currentState.FrontFaceStencil - }); + DepthWriteEnabled = _currentState.DepthWriteEnabled + }; + + if (_currentState.StencilTestEnabled) + { + descriptor.BackFaceStencil = _currentState.BackFaceStencil; + descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; + } + + _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); // Inline Update diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 29f348cac..0ef710011 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System; @@ -64,8 +65,7 @@ namespace Ryujinx.Graphics.Metal Add(Format.R32G32B32A32Sint, MTLPixelFormat.RGBA32Sint); Add(Format.S8Uint, MTLPixelFormat.Stencil8); Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm); - // Approximate - Add(Format.S8UintD24Unorm, MTLPixelFormat.BGRA8Unorm); + // Add(Format.S8UintD24Unorm, MTLPixelFormat.BGRA8Unorm); Add(Format.D32Float, MTLPixelFormat.Depth32Float); Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24UnormStencil8); Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8); @@ -181,5 +181,17 @@ namespace Ryujinx.Graphics.Metal return mtlFormat; } + + public static MTLPixelFormat PackedStencilToXFormat(MTLPixelFormat format) + { + switch (format) + { + case MTLPixelFormat.Depth24UnormStencil8: return MTLPixelFormat.X24Stencil8; + case MTLPixelFormat.Depth32FloatStencil8: return MTLPixelFormat.X32Stencil8; + default: + Logger.Warning?.PrintMsg(LogClass.Gpu, $"Attempted to get stencil format for non packed format {format}!"); + return MTLPixelFormat.Invalid; + } + } } } -- 2.47.1 From 95af212cfc6fd4411f10998da700af21a23fb21f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 20:27:27 -0400 Subject: [PATCH 124/368] Fix present --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 +- .../EncoderStateManager.cs | 66 ++++++++++--------- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 1 + src/Ryujinx.Graphics.Metal/Pipeline.cs | 6 +- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index ed512125b..35b726e25 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public struct EncoderState { + public const int MaxColorAttachments = 8; + public MTLFunction? VertexFunction = null; public MTLFunction? FragmentFunction = null; @@ -43,7 +45,7 @@ namespace Ryujinx.Graphics.Metal // Changes to attachments take recreation! public MTLTexture DepthStencil = default; - public MTLTexture[] RenderTargets = []; + public MTLTexture[] RenderTargets = new MTLTexture[MaxColorAttachments]; public MTLVertexDescriptor VertexDescriptor = new(); public EncoderState() { } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 49c3b1ee8..341490354 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -48,8 +48,7 @@ namespace Ryujinx.Graphics.Metal var renderPassDescriptor = new MTLRenderPassDescriptor(); var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - const int MaxColorAttachments = 8; - for (int i = 0; i < MaxColorAttachments; i++) + for (int i = 0; i < EncoderState.MaxColorAttachments; i++) { if (_currentState.RenderTargets[i] != IntPtr.Zero) { @@ -70,40 +69,43 @@ namespace Ryujinx.Graphics.Metal var depthAttachment = renderPassDescriptor.DepthAttachment; var stencilAttachment = renderPassDescriptor.StencilAttachment; - switch (_currentState.DepthStencil.PixelFormat) + if (_currentState.DepthStencil != IntPtr.Zero) { - // Depth Only Attachment - case MTLPixelFormat.Depth16Unorm: - case MTLPixelFormat.Depth32Float: - depthAttachment.Texture = _currentState.DepthStencil; - depthAttachment.LoadAction = MTLLoadAction.Load; - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; + switch (_currentState.DepthStencil.PixelFormat) + { + // Depth Only Attachment + case MTLPixelFormat.Depth16Unorm: + case MTLPixelFormat.Depth32Float: + depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.LoadAction = MTLLoadAction.Load; + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; - // Stencil Only Attachment - case MTLPixelFormat.Stencil8: - stencilAttachment.Texture = _currentState.DepthStencil; - stencilAttachment.LoadAction = MTLLoadAction.Load; - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; + // Stencil Only Attachment + case MTLPixelFormat.Stencil8: + stencilAttachment.Texture = _currentState.DepthStencil; + stencilAttachment.LoadAction = MTLLoadAction.Load; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; - // Combined Attachment - case MTLPixelFormat.Depth24UnormStencil8: - case MTLPixelFormat.Depth32FloatStencil8: - depthAttachment.Texture = _currentState.DepthStencil; - depthAttachment.LoadAction = MTLLoadAction.Load; + // Combined Attachment + case MTLPixelFormat.Depth24UnormStencil8: + case MTLPixelFormat.Depth32FloatStencil8: + depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.LoadAction = MTLLoadAction.Load; - var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.PixelFormat); - var stencilView = _currentState.DepthStencil.NewTextureView(unpackedFormat); - stencilAttachment.Texture = stencilView; - stencilAttachment.LoadAction = MTLLoadAction.Load; + var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.PixelFormat); + var stencilView = _currentState.DepthStencil.NewTextureView(unpackedFormat); + stencilAttachment.Texture = stencilView; + stencilAttachment.LoadAction = MTLLoadAction.Load; - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; - default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); - break; + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; + default: + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); + break; + } } renderPipelineDescriptor.VertexDescriptor = _currentState.VertexDescriptor; @@ -187,7 +189,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) { - _currentState.RenderTargets = new MTLTexture[colors.Length]; + _currentState.RenderTargets = new MTLTexture[EncoderState.MaxColorAttachments]; for (int i = 0; i < colors.Length; i++) { diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 7342a363b..4cd230dbc 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -43,6 +43,7 @@ namespace Ryujinx.Graphics.Metal { var layer = _getMetalLayer(); layer.Device = _device; + layer.FramebufferOnly = false; _window = new Window(this, layer); _pipeline = new Pipeline(_device, _queue); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 1b29a8562..7ec0384b4 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -158,7 +158,11 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager.SwapStates(); - // _helperShader.BlitColor(tex, drawable.Texture); + // TODO: Clean this up + var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); + var dest = new Texture(_device, this, textureInfo, drawable.Texture, 0, 0); + + _helperShader.BlitColor(tex, dest); _commandBuffer.PresentDrawable(drawable); _commandBuffer.Commit(); -- 2.47.1 From 3c1ef061513859d639ed8c895c6bcb7e122963fe Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 20:40:37 -0400 Subject: [PATCH 125/368] Format --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 13 ++++++------- src/Ryujinx.Graphics.Metal/FormatTable.cs | 6 ++++-- src/Ryujinx.Graphics.Metal/HelperShader.cs | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 341490354..e2ad3bb13 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -14,16 +14,15 @@ namespace Ryujinx.Graphics.Metal struct EncoderStateManager { private readonly MTLDevice _device; - private Pipeline _pipeline; + private readonly Pipeline _pipeline; private EncoderState _currentState = new(); private EncoderState _backState = new(); - // Public accessors - public MTLBuffer IndexBuffer => _currentState.IndexBuffer; - public MTLIndexType IndexType => _currentState.IndexType; - public ulong IndexBufferOffset => _currentState.IndexBufferOffset; - public PrimitiveTopology Topology => _currentState.Topology; + public readonly MTLBuffer IndexBuffer => _currentState.IndexBuffer; + public readonly MTLIndexType IndexType => _currentState.IndexType; + public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; + public readonly PrimitiveTopology Topology => _currentState.Topology; public EncoderStateManager(MTLDevice device, Pipeline pipeline) { @@ -495,7 +494,7 @@ namespace Ryujinx.Graphics.Metal } // Inlineable - public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, MTLTexture texture, MTLSamplerState sampler) + public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, MTLTexture texture, MTLSamplerState sampler) { switch (stage) { diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 0ef710011..3014cdafd 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -186,8 +186,10 @@ namespace Ryujinx.Graphics.Metal { switch (format) { - case MTLPixelFormat.Depth24UnormStencil8: return MTLPixelFormat.X24Stencil8; - case MTLPixelFormat.Depth32FloatStencil8: return MTLPixelFormat.X32Stencil8; + case MTLPixelFormat.Depth24UnormStencil8: + return MTLPixelFormat.X24Stencil8; + case MTLPixelFormat.Depth32FloatStencil8: + return MTLPixelFormat.X32Stencil8; default: Logger.Warning?.PrintMsg(LogClass.Gpu, $"Attempted to get stencil format for non packed format {format}!"); return MTLPixelFormat.Invalid; diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index a700cd68a..b4ddfe02c 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -19,8 +19,8 @@ namespace Ryujinx.Graphics.Metal class HelperShader : IDisposable { private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders"; + private readonly Pipeline _pipeline; private MTLDevice _device; - private Pipeline _pipeline; private readonly IProgram _programColorBlit; private readonly IProgram _programColorClearF; -- 2.47.1 From 401ad1f9837a4eca7c162323756dff3d6eef3a0e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 20:47:45 -0400 Subject: [PATCH 126/368] Make Texture Volatile on dispose --- src/Ryujinx.Graphics.Metal/Texture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 5ce1dd2ed..63e51305e 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -327,12 +327,12 @@ namespace Ryujinx.Graphics.Metal public void Release() { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + Dispose(); } public void Dispose() { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + MTLTexture.SetPurgeableState(MTLPurgeableState.Volatile); } } } -- 2.47.1 From 0d5292ff8c21a73444f990b8e69f699de28fcffa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 21:02:49 -0400 Subject: [PATCH 127/368] Ignore SetDepthMode --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 7ec0384b4..f8c5758da 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Metal public void SetDepthMode(DepthMode mode) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // Metal does not support depth clip control. } public void SetDepthTest(DepthTestDescriptor depthTest) -- 2.47.1 From 2587e1ff22a79d1e967b48df736322bf6a821c1e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 21:07:05 -0400 Subject: [PATCH 128/368] Be consistent with things that lack support --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index f8c5758da..5819e941a 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Metal public void SetBlendState(AdvancedBlendDescriptor blend) { - Logger.Warning?.Print(LogClass.Gpu, "Advanced blend is not supported in Metal!"); + // Metal does not support advanced blend. } public void SetBlendState(int index, BlendDescriptor blend) @@ -361,14 +361,12 @@ namespace Ryujinx.Graphics.Metal public void SetLineParameters(float width, bool smooth) { - // Not supported in Metal - Logger.Warning?.Print(LogClass.Gpu, "Wide-line is not supported without private Metal API"); + // Metal does not support wide-lines. } public void SetLogicOpState(bool enable, LogicalOp op) { - // Not supported in Metal - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // Metal does not support logic operations. } public void SetMultisampleState(MultisampleDescriptor multisample) @@ -388,8 +386,7 @@ namespace Ryujinx.Graphics.Metal public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) { - // Not supported in Metal - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // Metal does not support polygon mode. } public void SetPrimitiveRestart(bool enable, int index) @@ -534,20 +531,17 @@ namespace Ryujinx.Graphics.Metal public void BeginTransformFeedback(PrimitiveTopology topology) { - // Metal does not support Transform Feedback - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // Metal does not support transform feedback. } public void EndTransformFeedback() { - // Metal does not support Transform Feedback - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // Metal does not support transform feedback. } public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) { - // Metal does not support Transform Feedback - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // Metal does not support transform feedback. } public void Dispose() -- 2.47.1 From 15051d6e56669e449b325af6dd11cbb3231bb156 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 21:20:15 -0400 Subject: [PATCH 129/368] Implement SetBlendState --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 2 ++ .../EncoderStateManager.cs | 30 ++++++++++++++++++- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 35b726e25..248acd364 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -47,6 +47,8 @@ namespace Ryujinx.Graphics.Metal public MTLTexture DepthStencil = default; public MTLTexture[] RenderTargets = new MTLTexture[MaxColorAttachments]; public MTLVertexDescriptor VertexDescriptor = new(); + public Dictionary BlendDescriptors = new(); + public ColorF BlendColor = new(); public EncoderState() { } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e2ad3bb13..88e6330ed 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -56,12 +56,22 @@ namespace Ryujinx.Graphics.Metal passAttachment.LoadAction = MTLLoadAction.Load; var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - pipelineAttachment.SetBlendingEnabled(true); pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + + if (_currentState.BlendDescriptors.TryGetValue(i, out BlendDescriptor blendDescriptor)) + { + pipelineAttachment.SetBlendingEnabled(blendDescriptor.Enable); + pipelineAttachment.AlphaBlendOperation = blendDescriptor.AlphaOp.Convert(); + pipelineAttachment.RgbBlendOperation = blendDescriptor.ColorOp.Convert(); + pipelineAttachment.SourceAlphaBlendFactor = blendDescriptor.AlphaSrcFactor.Convert(); + pipelineAttachment.DestinationAlphaBlendFactor = blendDescriptor.AlphaDstFactor.Convert(); + pipelineAttachment.SourceRGBBlendFactor = blendDescriptor.ColorSrcFactor.Convert(); + pipelineAttachment.DestinationRGBBlendFactor = blendDescriptor.ColorDstFactor.Convert(); + } } } @@ -136,6 +146,12 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetRenderPipelineState(pipelineState); + renderCommandEncoder.SetBlendColor( + _currentState.BlendColor.Red, + _currentState.BlendColor.Green, + _currentState.BlendColor.Blue, + _currentState.BlendColor.Alpha); + SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); SetScissors(renderCommandEncoder, _currentState.Scissors); SetViewports(renderCommandEncoder, _currentState.Viewports); @@ -236,6 +252,18 @@ namespace Ryujinx.Graphics.Metal } } + public void UpdateBlendDescriptors(int index, BlendDescriptor blend) + { + _currentState.BlendDescriptors.Add(index, blend); + _currentState.BlendColor = blend.BlendConstant; + + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + // Inlineable public void UpdateStencilState(StencilTestDescriptor stencilTest) { diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 5819e941a..c7b926611 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -311,7 +311,7 @@ namespace Ryujinx.Graphics.Metal public void SetBlendState(int index, BlendDescriptor blend) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _encoderStateManager.UpdateBlendDescriptors(index, blend); } public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) -- 2.47.1 From 4f356b411766361077360615bedde889cc2364ac Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 21:29:46 -0400 Subject: [PATCH 130/368] Implement SetDepthClamp --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 1 + .../EncoderStateManager.cs | 19 +++++++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 248acd364..b5d6bcade 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.Metal public MTLDepthStencilState? DepthStencilState = null; + public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip; public MTLCompareFunction DepthCompareFunction = MTLCompareFunction.Always; public bool DepthWriteEnabled = false; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 88e6330ed..7acb99973 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -153,6 +153,7 @@ namespace Ryujinx.Graphics.Metal _currentState.BlendColor.Alpha); SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); + SetDepthClamp(renderCommandEncoder, _currentState.DepthClipMode); SetScissors(renderCommandEncoder, _currentState.Scissors); SetViewports(renderCommandEncoder, _currentState.Viewports); SetBuffers(renderCommandEncoder, _currentState.VertexBuffers); @@ -341,6 +342,19 @@ namespace Ryujinx.Graphics.Metal } } + // Inlineable + public void UpdateDepthClamp(bool clamp) + { + _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + } + } + // Inlineable public void UpdateScissors(ReadOnlySpan> regions) { @@ -552,6 +566,11 @@ namespace Ryujinx.Graphics.Metal } } + private static void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder, MTLDepthClipMode depthClipMode) + { + renderCommandEncoder.SetDepthClipMode(depthClipMode); + } + private unsafe static void SetScissors(MTLRenderCommandEncoder renderCommandEncoder, MTLScissorRect[] scissors) { if (scissors.Length > 0) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index c7b926611..f48efaed8 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -321,7 +321,7 @@ namespace Ryujinx.Graphics.Metal public void SetDepthClamp(bool clamp) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _encoderStateManager.UpdateDepthClamp(clamp); } public void SetDepthMode(DepthMode mode) -- 2.47.1 From c51c8bdae69f73863f0e21db91f5234f040440b4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 22:06:53 -0400 Subject: [PATCH 131/368] Fix Vertex Attributes in Wonder & Kirby --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 5 +- .../EncoderStateManager.cs | 100 +++++++++++------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index b5d6bcade..fabbe1c4f 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -19,7 +19,6 @@ namespace Ryujinx.Graphics.Metal public Dictionary VertexTextures = new(); public Dictionary VertexSamplers = new(); - public List VertexBuffers = []; public List UniformBuffers = []; public List StorageBuffers = []; @@ -47,10 +46,12 @@ namespace Ryujinx.Graphics.Metal // Changes to attachments take recreation! public MTLTexture DepthStencil = default; public MTLTexture[] RenderTargets = new MTLTexture[MaxColorAttachments]; - public MTLVertexDescriptor VertexDescriptor = new(); public Dictionary BlendDescriptors = new(); public ColorF BlendColor = new(); + public VertexBufferDescriptor[] VertexBuffers = []; + public VertexAttribDescriptor[] VertexAttribs = []; + public EncoderState() { } } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 7acb99973..5da579cc9 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Metal } } - renderPipelineDescriptor.VertexDescriptor = _currentState.VertexDescriptor; + renderPipelineDescriptor.VertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); if (_currentState.VertexFunction != null) { @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Metal SetDepthClamp(renderCommandEncoder, _currentState.DepthClipMode); SetScissors(renderCommandEncoder, _currentState.Scissors); SetViewports(renderCommandEncoder, _currentState.Viewports); - SetBuffers(renderCommandEncoder, _currentState.VertexBuffers); + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); SetCullMode(renderCommandEncoder, _currentState.CullMode); @@ -231,20 +231,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) { - for (int i = 0; i < vertexAttribs.Length; i++) - { - if (!vertexAttribs[i].IsZero) - { - // TODO: Format should not be hardcoded - var attrib = _currentState.VertexDescriptor.Attributes.Object((ulong)i); - attrib.Format = MTLVertexFormat.Float4; - attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; - attrib.Offset = (ulong)vertexAttribs[i].Offset; - - var layout = _currentState.VertexDescriptor.Layouts.Object((ulong)vertexAttribs[i].BufferIndex); - layout.Stride = 1; - } - } + _currentState.VertexAttribs = vertexAttribs.ToArray(); // Requires recreating pipeline if (_pipeline.CurrentEncoderType == EncoderType.Render) @@ -255,7 +242,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateBlendDescriptors(int index, BlendDescriptor blend) { - _currentState.BlendDescriptors.Add(index, blend); + _currentState.BlendDescriptors[index] = blend; _currentState.BlendColor = blend.BlendConstant; // Requires recreating pipeline @@ -422,33 +409,14 @@ namespace Ryujinx.Graphics.Metal } } - // Inlineable public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) { - _currentState.VertexBuffers = []; + _currentState.VertexBuffers = vertexBuffers.ToArray(); - for (int i = 0; i < vertexBuffers.Length; i++) + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) { - if (vertexBuffers[i].Stride != 0) - { - var layout = _currentState.VertexDescriptor.Layouts.Object((ulong)i); - layout.Stride = (ulong)vertexBuffers[i].Stride; - - _currentState.VertexBuffers.Add(new BufferInfo - { - Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(), - Offset = vertexBuffers[i].Buffer.Offset, - Index = i - }); - } - } - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetBuffers(renderCommandEncoder, _currentState.VertexBuffers); + _pipeline.EndCurrentPass(); } } @@ -593,6 +561,58 @@ namespace Ryujinx.Graphics.Metal } } + private static MTLVertexDescriptor BuildVertexDescriptor(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) + { + var vertexDescriptor = new MTLVertexDescriptor(); + + var usedIndexes = new List(); + + // TODO: Handle 'zero' buffers + for (int i = 0; i < attribDescriptors.Length; i++) + { + var attrib = vertexDescriptor.Attributes.Object((ulong)i); + // TODO: Format should not be hardcoded + attrib.Format = MTLVertexFormat.Float4; + usedIndexes.Add(attribDescriptors[i].BufferIndex); + attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; + attrib.Offset = (ulong)attribDescriptors[i].Offset; + } + + for (int i = 0; i < bufferDescriptors.Length; i++) + { + if (usedIndexes.Contains(i)) + { + var layout = vertexDescriptor.Layouts.Object((ulong)i); + layout.Stride = (ulong)bufferDescriptors[i].Stride; + } + else + { + var layout = vertexDescriptor.Layouts.Object((ulong)i); + layout.Stride = 0; + } + } + + return vertexDescriptor; + } + + private static void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) + { + var buffers = new List(); + + + for (int i = 0; i < bufferDescriptors.Length; i++) + { + buffers.Add(new BufferInfo + { + Handle = bufferDescriptors[i].Buffer.Handle.ToIntPtr(), + Offset = bufferDescriptors[i].Buffer.Offset, + Index = i + }); + } + + SetBuffers(renderCommandEncoder, buffers); + } + private static void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) { foreach (var buffer in buffers) -- 2.47.1 From 46cc993f9add116e630ff34d9466fda488b665bd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 22:17:18 -0400 Subject: [PATCH 132/368] Cleanup --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 5da579cc9..6b8689877 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -580,16 +580,8 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < bufferDescriptors.Length; i++) { - if (usedIndexes.Contains(i)) - { - var layout = vertexDescriptor.Layouts.Object((ulong)i); - layout.Stride = (ulong)bufferDescriptors[i].Stride; - } - else - { - var layout = vertexDescriptor.Layouts.Object((ulong)i); - layout.Stride = 0; - } + var layout = vertexDescriptor.Layouts.Object((ulong)i); + layout.Stride = usedIndexes.Contains(i) ? (ulong)bufferDescriptors[i].Stride : 0; } return vertexDescriptor; -- 2.47.1 From 2c474050f8d9aba1b66e3b0cc3bd9907add40714 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 22:27:18 -0400 Subject: [PATCH 133/368] Be smart and use a bitmask not a list --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 6b8689877..e13b3cde6 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -564,8 +564,7 @@ namespace Ryujinx.Graphics.Metal private static MTLVertexDescriptor BuildVertexDescriptor(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) { var vertexDescriptor = new MTLVertexDescriptor(); - - var usedIndexes = new List(); + uint indexMask = 0; // TODO: Handle 'zero' buffers for (int i = 0; i < attribDescriptors.Length; i++) @@ -573,7 +572,7 @@ namespace Ryujinx.Graphics.Metal var attrib = vertexDescriptor.Attributes.Object((ulong)i); // TODO: Format should not be hardcoded attrib.Format = MTLVertexFormat.Float4; - usedIndexes.Add(attribDescriptors[i].BufferIndex); + indexMask |= 1u << attribDescriptors[i].BufferIndex; attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; attrib.Offset = (ulong)attribDescriptors[i].Offset; } @@ -581,7 +580,7 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < bufferDescriptors.Length; i++) { var layout = vertexDescriptor.Layouts.Object((ulong)i); - layout.Stride = usedIndexes.Contains(i) ? (ulong)bufferDescriptors[i].Stride : 0; + layout.Stride = (indexMask & (1u << i)) != 0 ? (ulong)bufferDescriptors[i].Stride : 0; } return vertexDescriptor; -- 2.47.1 From 6324569dd2790c10425d12a32e9df6c68b5ea80a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 22:29:03 -0400 Subject: [PATCH 134/368] Remove rebase garbage --- Directory.Packages.props.orig | 61 ----------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 Directory.Packages.props.orig diff --git a/Directory.Packages.props.orig b/Directory.Packages.props.orig deleted file mode 100644 index 14097e354..000000000 --- a/Directory.Packages.props.orig +++ /dev/null @@ -1,61 +0,0 @@ - - - true - - - - - - - - - - - - - - - - - - - -<<<<<<< HEAD - -======= - ->>>>>>> 546c1ffc0 (Fix some rebase errors) - - - - - - - - - - - - - - - - - - - - - - -<<<<<<< HEAD - -======= - ->>>>>>> 546c1ffc0 (Fix some rebase errors) - - - - - - - -- 2.47.1 From f3d314104fc3e4c8d3931ecbfd113d8299c55932 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 08:08:12 +0200 Subject: [PATCH 135/368] don't end render pass when not neccessary --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 16 ++ .../EncoderStateManager.cs | 210 ++++++------------ src/Ryujinx.Graphics.Metal/Pipeline.cs | 13 +- 3 files changed, 97 insertions(+), 142 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index fabbe1c4f..f723bb0ac 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -5,6 +5,19 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + // TODO: use this (unused right now) + public struct DirtyFlags + { + public bool Pipeline = false; + public bool DepthStencil = false; + public bool CullMode = false; + public bool Winding = false; + public bool Viewport = false; + public bool Scissor = false; + + public DirtyFlags() { } + } + [SupportedOSPlatform("macos")] public struct EncoderState { @@ -52,6 +65,9 @@ namespace Ryujinx.Graphics.Metal public VertexBufferDescriptor[] VertexBuffers = []; public VertexAttribDescriptor[] VertexAttribs = []; + // Dirty flags + public DirtyFlags Dirty = new(); + public EncoderState() { } } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e13b3cde6..b26b8803e 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -54,7 +54,80 @@ namespace Ryujinx.Graphics.Metal var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); passAttachment.Texture = _currentState.RenderTargets[i]; passAttachment.LoadAction = MTLLoadAction.Load; + } + } + var depthAttachment = renderPassDescriptor.DepthAttachment; + var stencilAttachment = renderPassDescriptor.StencilAttachment; + + if (_currentState.DepthStencil != IntPtr.Zero) + { + switch (_currentState.DepthStencil.PixelFormat) + { + // Depth Only Attachment + case MTLPixelFormat.Depth16Unorm: + case MTLPixelFormat.Depth32Float: + depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.LoadAction = MTLLoadAction.Load; + break; + + // Stencil Only Attachment + case MTLPixelFormat.Stencil8: + stencilAttachment.Texture = _currentState.DepthStencil; + stencilAttachment.LoadAction = MTLLoadAction.Load; + break; + + // Combined Attachment + case MTLPixelFormat.Depth24UnormStencil8: + case MTLPixelFormat.Depth32FloatStencil8: + depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.LoadAction = MTLLoadAction.Load; + + var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.PixelFormat); + var stencilView = _currentState.DepthStencil.NewTextureView(unpackedFormat); + stencilAttachment.Texture = stencilView; + stencilAttachment.LoadAction = MTLLoadAction.Load; + break; + default: + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); + break; + } + } + + // Initialise Encoder + + var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); + + // TODO: set dirty flags all to true + + return renderCommandEncoder; + } + + public void RebindState(MTLRenderCommandEncoder renderCommandEncoder) + { + SetPipelineState(renderCommandEncoder); + SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); + SetDepthClamp(renderCommandEncoder, _currentState.DepthClipMode); + SetScissors(renderCommandEncoder, _currentState.Scissors); + SetViewports(renderCommandEncoder, _currentState.Viewports); + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); + SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + SetCullMode(renderCommandEncoder, _currentState.CullMode); + SetFrontFace(renderCommandEncoder, _currentState.Winding); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + + _currentState.Dirty = new(); + } + + private void SetPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + + for (int i = 0; i < EncoderState.MaxColorAttachments; i++) + { + if (_currentState.RenderTargets[i] != IntPtr.Zero) + { var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; @@ -75,9 +148,6 @@ namespace Ryujinx.Graphics.Metal } } - var depthAttachment = renderPassDescriptor.DepthAttachment; - var stencilAttachment = renderPassDescriptor.StencilAttachment; - if (_currentState.DepthStencil != IntPtr.Zero) { switch (_currentState.DepthStencil.PixelFormat) @@ -85,29 +155,17 @@ namespace Ryujinx.Graphics.Metal // Depth Only Attachment case MTLPixelFormat.Depth16Unorm: case MTLPixelFormat.Depth32Float: - depthAttachment.Texture = _currentState.DepthStencil; - depthAttachment.LoadAction = MTLLoadAction.Load; renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; break; // Stencil Only Attachment case MTLPixelFormat.Stencil8: - stencilAttachment.Texture = _currentState.DepthStencil; - stencilAttachment.LoadAction = MTLLoadAction.Load; renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; break; // Combined Attachment case MTLPixelFormat.Depth24UnormStencil8: case MTLPixelFormat.Depth32FloatStencil8: - depthAttachment.Texture = _currentState.DepthStencil; - depthAttachment.LoadAction = MTLLoadAction.Load; - - var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.PixelFormat); - var stencilView = _currentState.DepthStencil.NewTextureView(unpackedFormat); - stencilAttachment.Texture = stencilView; - stencilAttachment.LoadAction = MTLLoadAction.Load; - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; break; @@ -125,7 +183,7 @@ namespace Ryujinx.Graphics.Metal } else { - return new(IntPtr.Zero); + return; } if (_currentState.FragmentFunction != null) @@ -140,10 +198,6 @@ namespace Ryujinx.Graphics.Metal Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); } - // Initialise Encoder - - var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); - renderCommandEncoder.SetRenderPipelineState(pipelineState); renderCommandEncoder.SetBlendColor( @@ -151,20 +205,6 @@ namespace Ryujinx.Graphics.Metal _currentState.BlendColor.Green, _currentState.BlendColor.Blue, _currentState.BlendColor.Alpha); - - SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); - SetDepthClamp(renderCommandEncoder, _currentState.DepthClipMode); - SetScissors(renderCommandEncoder, _currentState.Scissors); - SetViewports(renderCommandEncoder, _currentState.Viewports); - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - SetCullMode(renderCommandEncoder, _currentState.CullMode); - SetFrontFace(renderCommandEncoder, _currentState.Winding); - SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); - SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); - - return renderCommandEncoder; } public void UpdateIndexBuffer(BufferRange buffer, IndexType type) @@ -195,12 +235,6 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexFunction = prg.VertexFunction; _currentState.FragmentFunction = prg.FragmentFunction; - - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } } public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) @@ -232,24 +266,12 @@ namespace Ryujinx.Graphics.Metal public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) { _currentState.VertexAttribs = vertexAttribs.ToArray(); - - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } } public void UpdateBlendDescriptors(int index, BlendDescriptor blend) { _currentState.BlendDescriptors[index] = blend; _currentState.BlendColor = blend.BlendConstant; - - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } } // Inlineable @@ -290,14 +312,6 @@ namespace Ryujinx.Graphics.Metal } _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); - } } // Inlineable @@ -319,27 +333,12 @@ namespace Ryujinx.Graphics.Metal } _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); - } } // Inlineable public void UpdateDepthClamp(bool clamp) { _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - } } // Inlineable @@ -366,14 +365,6 @@ namespace Ryujinx.Graphics.Metal y = (ulong)region.Y }; } - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetScissors(renderCommandEncoder, _currentState.Scissors); - } } // Inlineable @@ -399,25 +390,11 @@ namespace Ryujinx.Graphics.Metal zfar = Clamp(viewport.DepthFar) }; } - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetViewports(renderCommandEncoder, _currentState.Viewports); - } } public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) { _currentState.VertexBuffers = vertexBuffers.ToArray(); - - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } } // Inlineable @@ -437,14 +414,6 @@ namespace Ryujinx.Graphics.Metal }); } } - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - } } // Inlineable @@ -465,42 +434,18 @@ namespace Ryujinx.Graphics.Metal }); } } - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - } } // Inlineable public void UpdateCullMode(bool enable, Face face) { _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetCullMode(renderCommandEncoder, _currentState.CullMode); - } } // Inlineable public void UpdateFrontFace(FrontFace frontFace) { _currentState.Winding = frontFace.Convert(); - - // Inline Update - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetFrontFace(renderCommandEncoder, _currentState.Winding); - } } // Inlineable @@ -517,13 +462,6 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexSamplers[binding] = sampler; break; } - - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); - SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); - } } private static void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder, MTLDepthStencilState? depthStencilState) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index f48efaed8..f1dcd19c0 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -48,15 +48,16 @@ namespace Ryujinx.Graphics.Metal public MTLRenderCommandEncoder GetOrCreateRenderEncoder() { - if (_currentEncoder != null) + if (_currentEncoder == null || _currentEncoderType != EncoderType.Render) { - if (_currentEncoderType == EncoderType.Render) - { - return new MTLRenderCommandEncoder(_currentEncoder.Value); - } + BeginRenderPass(); } - return BeginRenderPass(); + var renderCommandEncoder = new MTLRenderCommandEncoder(_currentEncoder.Value); + + _encoderStateManager.RebindState(renderCommandEncoder); + + return renderCommandEncoder; } public MTLBlitCommandEncoder GetOrCreateBlitEncoder() -- 2.47.1 From bc9a26bbf27b9ce4eac4fcebd034d504c93e9c35 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 08:09:06 +0200 Subject: [PATCH 136/368] add todo notice --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index b26b8803e..e675d6459 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -105,6 +105,7 @@ namespace Ryujinx.Graphics.Metal public void RebindState(MTLRenderCommandEncoder renderCommandEncoder) { + // TODO: only rebind the dirty state SetPipelineState(renderCommandEncoder); SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); SetDepthClamp(renderCommandEncoder, _currentState.DepthClipMode); -- 2.47.1 From b5f15de64ff7a9653febe6def427b675ce334ed3 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 09:10:14 +0200 Subject: [PATCH 137/368] mark state as dirty --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 9 ++ .../EncoderStateManager.cs | 111 ++++++++++++------ 2 files changed, 84 insertions(+), 36 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index f723bb0ac..f87542775 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -16,6 +16,15 @@ namespace Ryujinx.Graphics.Metal public bool Scissor = false; public DirtyFlags() { } + + public void MarkAll() { + Pipeline = true; + DepthStencil = true; + CullMode = true; + Winding = true; + Viewport = true; + Scissor = true; + } } [SupportedOSPlatform("macos")] diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e675d6459..069c1da63 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); - // TODO: set dirty flags all to true + _currentState.Dirty.MarkAll(); return renderCommandEncoder; } @@ -107,15 +107,15 @@ namespace Ryujinx.Graphics.Metal { // TODO: only rebind the dirty state SetPipelineState(renderCommandEncoder); - SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); - SetDepthClamp(renderCommandEncoder, _currentState.DepthClipMode); - SetScissors(renderCommandEncoder, _currentState.Scissors); - SetViewports(renderCommandEncoder, _currentState.Viewports); + SetDepthStencilState(renderCommandEncoder); + SetDepthClamp(renderCommandEncoder); + SetCullMode(renderCommandEncoder); + SetFrontFace(renderCommandEncoder); + SetViewports(renderCommandEncoder); + SetScissors(renderCommandEncoder); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - SetCullMode(renderCommandEncoder, _currentState.CullMode); - SetFrontFace(renderCommandEncoder, _currentState.Winding); SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); @@ -236,6 +236,9 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexFunction = prg.VertexFunction; _currentState.FragmentFunction = prg.FragmentFunction; + + // Mark dirty + _currentState.Dirty.Pipeline = true; } public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) @@ -267,6 +270,9 @@ namespace Ryujinx.Graphics.Metal public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) { _currentState.VertexAttribs = vertexAttribs.ToArray(); + + // Mark dirty + _currentState.Dirty.Pipeline = true; } public void UpdateBlendDescriptors(int index, BlendDescriptor blend) @@ -313,6 +319,9 @@ namespace Ryujinx.Graphics.Metal } _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); + + // Mark dirty + _currentState.Dirty.DepthStencil = true; } // Inlineable @@ -334,6 +343,9 @@ namespace Ryujinx.Graphics.Metal } _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); + + // Mark dirty + _currentState.Dirty.DepthStencil = true; } // Inlineable @@ -366,6 +378,9 @@ namespace Ryujinx.Graphics.Metal y = (ulong)region.Y }; } + + // Mark dirty + _currentState.Dirty.Scissor = true; } // Inlineable @@ -391,6 +406,9 @@ namespace Ryujinx.Graphics.Metal zfar = Clamp(viewport.DepthFar) }; } + + // Mark dirty + _currentState.Dirty.Viewport = true; } public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) @@ -441,12 +459,18 @@ namespace Ryujinx.Graphics.Metal public void UpdateCullMode(bool enable, Face face) { _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; + + // Mark dirty + _currentState.Dirty.CullMode = true; } // Inlineable public void UpdateFrontFace(FrontFace frontFace) { _currentState.Winding = frontFace.Convert(); + + // Mark dirty + _currentState.Dirty.Winding = true; } // Inlineable @@ -465,42 +489,51 @@ namespace Ryujinx.Graphics.Metal } } - private static void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder, MTLDepthStencilState? depthStencilState) + private void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { - if (depthStencilState != null) + if (_currentState.Dirty.DepthStencil) { - renderCommandEncoder.SetDepthStencilState(depthStencilState.Value); - } - } - - private static void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder, MTLDepthClipMode depthClipMode) - { - renderCommandEncoder.SetDepthClipMode(depthClipMode); - } - - private unsafe static void SetScissors(MTLRenderCommandEncoder renderCommandEncoder, MTLScissorRect[] scissors) - { - if (scissors.Length > 0) - { - fixed (MTLScissorRect* pMtlScissors = scissors) + if (_currentState.DepthStencilState != null) { - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)scissors.Length); + renderCommandEncoder.SetDepthStencilState(_currentState.DepthStencilState.Value); } } } - private unsafe static void SetViewports(MTLRenderCommandEncoder renderCommandEncoder, MTLViewport[] viewports) + private void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder) { - if (viewports.Length > 0) + renderCommandEncoder.SetDepthClipMode(_currentState.DepthClipMode); + } + + private unsafe void SetScissors(MTLRenderCommandEncoder renderCommandEncoder) + { + if (_currentState.Dirty.Scissor) { - fixed (MTLViewport* pMtlViewports = viewports) + if (_currentState.Scissors.Length > 0) { - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); + fixed (MTLScissorRect* pMtlScissors = _currentState.Scissors) + { + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_currentState.Scissors.Length); + } } } } - private static MTLVertexDescriptor BuildVertexDescriptor(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) + private unsafe void SetViewports(MTLRenderCommandEncoder renderCommandEncoder) + { + if (_currentState.Dirty.Viewport) + { + if (_currentState.Viewports.Length > 0) + { + fixed (MTLViewport* pMtlViewports = _currentState.Viewports) + { + renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_currentState.Viewports.Length); + } + } + } + } + + private MTLVertexDescriptor BuildVertexDescriptor(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) { var vertexDescriptor = new MTLVertexDescriptor(); uint indexMask = 0; @@ -525,7 +558,7 @@ namespace Ryujinx.Graphics.Metal return vertexDescriptor; } - private static void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) + private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) { var buffers = new List(); @@ -543,7 +576,7 @@ namespace Ryujinx.Graphics.Metal SetBuffers(renderCommandEncoder, buffers); } - private static void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) + private void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) { foreach (var buffer in buffers) { @@ -556,17 +589,23 @@ namespace Ryujinx.Graphics.Metal } } - private static void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder, MTLCullMode cullMode) + private void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) { - renderCommandEncoder.SetCullMode(cullMode); + if (_currentState.Dirty.CullMode) + { + renderCommandEncoder.SetCullMode(_currentState.CullMode); + } } - private static void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder, MTLWinding winding) + private void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder) { - renderCommandEncoder.SetFrontFacingWinding(winding); + if (_currentState.Dirty.Winding) + { + renderCommandEncoder.SetFrontFacingWinding(_currentState.Winding); + } } - private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary textures, Dictionary samplers) + private void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary textures, Dictionary samplers) { foreach (var texture in textures) { -- 2.47.1 From 149141594f55c073c7304f58362bc6e38143746b Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 13:25:52 +0200 Subject: [PATCH 138/368] don't bind null vertex buffers --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 069c1da63..85d553433 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -565,12 +565,15 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < bufferDescriptors.Length; i++) { - buffers.Add(new BufferInfo + if (bufferDescriptors[i].Buffer.Handle.ToIntPtr() != IntPtr.Zero) { - Handle = bufferDescriptors[i].Buffer.Handle.ToIntPtr(), - Offset = bufferDescriptors[i].Buffer.Offset, - Index = i - }); + buffers.Add(new BufferInfo + { + Handle = bufferDescriptors[i].Buffer.Handle.ToIntPtr(), + Offset = bufferDescriptors[i].Buffer.Offset, + Index = i + }); + } } SetBuffers(renderCommandEncoder, buffers); -- 2.47.1 From f30aa98ce5cbb2b5720f57ac04e010ea9f578bce Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 13:32:09 +0200 Subject: [PATCH 139/368] fix: don't rebind pipeline unless dirty --- .../EncoderStateManager.cs | 147 +++++++++--------- 1 file changed, 75 insertions(+), 72 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 85d553433..154f0a6d4 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -123,89 +123,92 @@ namespace Ryujinx.Graphics.Metal } private void SetPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - - for (int i = 0; i < EncoderState.MaxColorAttachments; i++) + if (_currentState.Dirty.Pipeline) { - if (_currentState.RenderTargets[i] != IntPtr.Zero) - { - var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; - pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - if (_currentState.BlendDescriptors.TryGetValue(i, out BlendDescriptor blendDescriptor)) + for (int i = 0; i < EncoderState.MaxColorAttachments; i++) + { + if (_currentState.RenderTargets[i] != IntPtr.Zero) { - pipelineAttachment.SetBlendingEnabled(blendDescriptor.Enable); - pipelineAttachment.AlphaBlendOperation = blendDescriptor.AlphaOp.Convert(); - pipelineAttachment.RgbBlendOperation = blendDescriptor.ColorOp.Convert(); - pipelineAttachment.SourceAlphaBlendFactor = blendDescriptor.AlphaSrcFactor.Convert(); - pipelineAttachment.DestinationAlphaBlendFactor = blendDescriptor.AlphaDstFactor.Convert(); - pipelineAttachment.SourceRGBBlendFactor = blendDescriptor.ColorSrcFactor.Convert(); - pipelineAttachment.DestinationRGBBlendFactor = blendDescriptor.ColorDstFactor.Convert(); + var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); + pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; + pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + + if (_currentState.BlendDescriptors.TryGetValue(i, out BlendDescriptor blendDescriptor)) + { + pipelineAttachment.SetBlendingEnabled(blendDescriptor.Enable); + pipelineAttachment.AlphaBlendOperation = blendDescriptor.AlphaOp.Convert(); + pipelineAttachment.RgbBlendOperation = blendDescriptor.ColorOp.Convert(); + pipelineAttachment.SourceAlphaBlendFactor = blendDescriptor.AlphaSrcFactor.Convert(); + pipelineAttachment.DestinationAlphaBlendFactor = blendDescriptor.AlphaDstFactor.Convert(); + pipelineAttachment.SourceRGBBlendFactor = blendDescriptor.ColorSrcFactor.Convert(); + pipelineAttachment.DestinationRGBBlendFactor = blendDescriptor.ColorDstFactor.Convert(); + } } } - } - if (_currentState.DepthStencil != IntPtr.Zero) - { - switch (_currentState.DepthStencil.PixelFormat) + if (_currentState.DepthStencil != IntPtr.Zero) { - // Depth Only Attachment - case MTLPixelFormat.Depth16Unorm: - case MTLPixelFormat.Depth32Float: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; + switch (_currentState.DepthStencil.PixelFormat) + { + // Depth Only Attachment + case MTLPixelFormat.Depth16Unorm: + case MTLPixelFormat.Depth32Float: + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; - // Stencil Only Attachment - case MTLPixelFormat.Stencil8: - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; + // Stencil Only Attachment + case MTLPixelFormat.Stencil8: + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; - // Combined Attachment - case MTLPixelFormat.Depth24UnormStencil8: - case MTLPixelFormat.Depth32FloatStencil8: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; - default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); - break; + // Combined Attachment + case MTLPixelFormat.Depth24UnormStencil8: + case MTLPixelFormat.Depth32FloatStencil8: + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; + default: + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); + break; + } } + + renderPipelineDescriptor.VertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); + + if (_currentState.VertexFunction != null) + { + renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; + } + else + { + return; + } + + if (_currentState.FragmentFunction != null) + { + renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; + } + + var error = new NSError(IntPtr.Zero); + var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + renderCommandEncoder.SetRenderPipelineState(pipelineState); + + renderCommandEncoder.SetBlendColor( + _currentState.BlendColor.Red, + _currentState.BlendColor.Green, + _currentState.BlendColor.Blue, + _currentState.BlendColor.Alpha); } - - renderPipelineDescriptor.VertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); - - if (_currentState.VertexFunction != null) - { - renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; - } - else - { - return; - } - - if (_currentState.FragmentFunction != null) - { - renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; - } - - var error = new NSError(IntPtr.Zero); - var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - renderCommandEncoder.SetRenderPipelineState(pipelineState); - - renderCommandEncoder.SetBlendColor( - _currentState.BlendColor.Red, - _currentState.BlendColor.Green, - _currentState.BlendColor.Blue, - _currentState.BlendColor.Alpha); } public void UpdateIndexBuffer(BufferRange buffer, IndexType type) -- 2.47.1 From 5a6169b19d9351e072174ac27451ea45880c4e63 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 16:47:43 +0200 Subject: [PATCH 140/368] bring back inline updates for some state --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 8 -- .../EncoderStateManager.cs | 118 ++++++++++++------ 2 files changed, 81 insertions(+), 45 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index f87542775..a3f90b7c3 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -10,20 +10,12 @@ namespace Ryujinx.Graphics.Metal { public bool Pipeline = false; public bool DepthStencil = false; - public bool CullMode = false; - public bool Winding = false; - public bool Viewport = false; - public bool Scissor = false; public DirtyFlags() { } public void MarkAll() { Pipeline = true; DepthStencil = true; - CullMode = true; - Winding = true; - Viewport = true; - Scissor = true; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 154f0a6d4..baa3c8728 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -98,16 +98,10 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); + // Mark all state as dirty to ensure it is set on the encoder _currentState.Dirty.MarkAll(); - return renderCommandEncoder; - } - - public void RebindState(MTLRenderCommandEncoder renderCommandEncoder) - { - // TODO: only rebind the dirty state - SetPipelineState(renderCommandEncoder); - SetDepthStencilState(renderCommandEncoder); + // Rebind all the state SetDepthClamp(renderCommandEncoder); SetCullMode(renderCommandEncoder); SetFrontFace(renderCommandEncoder); @@ -119,6 +113,14 @@ namespace Ryujinx.Graphics.Metal SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + return renderCommandEncoder; + } + + public void RebindState(MTLRenderCommandEncoder renderCommandEncoder) + { + SetPipelineState(renderCommandEncoder); + SetDepthStencilState(renderCommandEncoder); + _currentState.Dirty = new(); } @@ -355,6 +357,13 @@ namespace Ryujinx.Graphics.Metal public void UpdateDepthClamp(bool clamp) { _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; + + // Inline update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetDepthClamp(renderCommandEncoder); + } } // Inlineable @@ -382,8 +391,12 @@ namespace Ryujinx.Graphics.Metal }; } - // Mark dirty - _currentState.Dirty.Scissor = true; + // Inline update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetScissors(renderCommandEncoder); + } } // Inlineable @@ -410,13 +423,27 @@ namespace Ryujinx.Graphics.Metal }; } - // Mark dirty - _currentState.Dirty.Viewport = true; + // Inline update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetViewports(renderCommandEncoder); + } } public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) { _currentState.VertexBuffers = vertexBuffers.ToArray(); + + // Inline update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); + } + + // Mark dirty + _currentState.Dirty.Pipeline = true; } // Inlineable @@ -436,6 +463,13 @@ namespace Ryujinx.Graphics.Metal }); } } + + // Inline Update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + } } // Inlineable @@ -456,6 +490,13 @@ namespace Ryujinx.Graphics.Metal }); } } + + // Inline Update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + } } // Inlineable @@ -463,8 +504,12 @@ namespace Ryujinx.Graphics.Metal { _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; - // Mark dirty - _currentState.Dirty.CullMode = true; + // Inline Update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetCullMode(renderCommandEncoder); + } } // Inlineable @@ -472,8 +517,12 @@ namespace Ryujinx.Graphics.Metal { _currentState.Winding = frontFace.Convert(); - // Mark dirty - _currentState.Dirty.Winding = true; + // Inline Update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetFrontFace(renderCommandEncoder); + } } // Inlineable @@ -490,6 +539,14 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexSamplers[binding] = sampler; break; } + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + // TODO: Only update the new ones + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + } } private void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) @@ -510,28 +567,22 @@ namespace Ryujinx.Graphics.Metal private unsafe void SetScissors(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.Scissor) + if (_currentState.Scissors.Length > 0) { - if (_currentState.Scissors.Length > 0) + fixed (MTLScissorRect* pMtlScissors = _currentState.Scissors) { - fixed (MTLScissorRect* pMtlScissors = _currentState.Scissors) - { - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_currentState.Scissors.Length); - } + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_currentState.Scissors.Length); } } } private unsafe void SetViewports(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.Viewport) + if (_currentState.Viewports.Length > 0) { - if (_currentState.Viewports.Length > 0) + fixed (MTLViewport* pMtlViewports = _currentState.Viewports) { - fixed (MTLViewport* pMtlViewports = _currentState.Viewports) - { - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_currentState.Viewports.Length); - } + renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_currentState.Viewports.Length); } } } @@ -565,7 +616,6 @@ namespace Ryujinx.Graphics.Metal { var buffers = new List(); - for (int i = 0; i < bufferDescriptors.Length; i++) { if (bufferDescriptors[i].Buffer.Handle.ToIntPtr() != IntPtr.Zero) @@ -597,18 +647,12 @@ namespace Ryujinx.Graphics.Metal private void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.CullMode) - { - renderCommandEncoder.SetCullMode(_currentState.CullMode); - } + renderCommandEncoder.SetCullMode(_currentState.CullMode); } private void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.Winding) - { - renderCommandEncoder.SetFrontFacingWinding(_currentState.Winding); - } + renderCommandEncoder.SetFrontFacingWinding(_currentState.Winding); } private void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary textures, Dictionary samplers) -- 2.47.1 From 9b99f55c4f5718a62d8a507325d3320a0d7c8d4f Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 16:51:05 +0200 Subject: [PATCH 141/368] style --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index baa3c8728..97abd93e3 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -464,7 +464,7 @@ namespace Ryujinx.Graphics.Metal } } - // Inline Update + // Inline update if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); @@ -491,7 +491,7 @@ namespace Ryujinx.Graphics.Metal } } - // Inline Update + // Inline update if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); @@ -504,7 +504,7 @@ namespace Ryujinx.Graphics.Metal { _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; - // Inline Update + // Inline update if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); @@ -517,7 +517,7 @@ namespace Ryujinx.Graphics.Metal { _currentState.Winding = frontFace.Convert(); - // Inline Update + // Inline update if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); -- 2.47.1 From ba4d6815ea655c62d689adacfecef6a8762ea5af Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 19 May 2024 10:32:24 -0400 Subject: [PATCH 142/368] Dont hardcode Vertex Format --- .../EncoderStateManager.cs | 3 +- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 31 ++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 97abd93e3..ad8714cf0 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -596,8 +596,7 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < attribDescriptors.Length; i++) { var attrib = vertexDescriptor.Attributes.Object((ulong)i); - // TODO: Format should not be hardcoded - attrib.Format = MTLVertexFormat.Float4; + attrib.Format = attribDescriptors[i].Format.Convert(); indexMask |= 1u << attribDescriptors[i].BufferIndex; attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; attrib.Offset = (ulong)attribDescriptors[i].Offset; diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index 428e90caa..dd9c22da7 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -194,7 +194,36 @@ namespace Ryujinx.Graphics.Metal SwizzleComponent.Green => MTLTextureSwizzle.Green, SwizzleComponent.Blue => MTLTextureSwizzle.Blue, SwizzleComponent.Alpha => MTLTextureSwizzle.Alpha, - _ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), MTLTextureSwizzle.Zero), + _ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), MTLTextureSwizzle.Zero) + }; + } + + public static MTLVertexFormat Convert(this Format format) + { + return format switch + { + Format.R16Float or Format.R32Float => MTLVertexFormat.Float, + Format.R16G16Float or Format.R32G32Float => MTLVertexFormat.Float2, + Format.R16G16B16Float or Format.R32G32B32Float or Format.R11G11B10Float => MTLVertexFormat.Float3, + Format.R16G16B16A16Float or Format.R32G32B32A32Float => MTLVertexFormat.Float4, + Format.R8Uint or Format.R16Uint or Format.R32Uint => MTLVertexFormat.UInt, + Format.R8G8Uint or Format.R16G16Uint or Format.R32G32Uint => MTLVertexFormat.UInt2, + Format.R8G8B8Uint or Format.R16G16B16Uint or Format.R32G32B32Uint => MTLVertexFormat.UInt3, + Format.R8G8B8A8Uint or Format.R16G16B16A16Uint or Format.R32G32B32A32Uint or Format.R10G10B10A2Uint => MTLVertexFormat.UInt4, + Format.R8Sint or Format.R16Sint or Format.R32Sint => MTLVertexFormat.Int, + Format.R8G8Sint or Format.R16G16Sint or Format.R32G32Sint => MTLVertexFormat.Int2, + Format.R8G8B8Sint or Format.R16G16B16Sint or Format.R32G32B32Sint => MTLVertexFormat.Int3, + Format.R8G8B8A8Sint or Format.R16G16B16A16Sint or Format.R32G32B32A32Sint => MTLVertexFormat.Int4, + Format.R8Unorm or Format.R16Unorm => MTLVertexFormat.UShortNormalized, + Format.R8G8Unorm or Format.R16G16Unorm => MTLVertexFormat.UShort2Normalized, + Format.R8G8B8Unorm or Format.R16G16B16Unorm => MTLVertexFormat.UShort3Normalized, + Format.R8G8B8A8Unorm or Format.R16G16B16A16Unorm or Format.R10G10B10A2Unorm => MTLVertexFormat.UShort4Normalized, + Format.R8Snorm or Format.R16Snorm => MTLVertexFormat.ShortNormalized, + Format.R8G8Snorm or Format.R16G16Snorm => MTLVertexFormat.Short2Normalized, + Format.R8G8B8Snorm or Format.R16G16B16Snorm => MTLVertexFormat.Short3Normalized, + Format.R8G8B8A8Snorm or Format.R16G16B16A16Snorm or Format.R10G10B10A2Snorm => MTLVertexFormat.Short4Normalized, + + _ => LogInvalidAndReturn(format, nameof(Format), MTLVertexFormat.Float4) }; } -- 2.47.1 From efe575c9b2767423048153d6ee44d1fa021721aa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 19 May 2024 10:44:08 -0400 Subject: [PATCH 143/368] Fix table --- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 71 ++++++++++++++------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index dd9c22da7..dbde36f5e 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -202,26 +202,57 @@ namespace Ryujinx.Graphics.Metal { return format switch { - Format.R16Float or Format.R32Float => MTLVertexFormat.Float, - Format.R16G16Float or Format.R32G32Float => MTLVertexFormat.Float2, - Format.R16G16B16Float or Format.R32G32B32Float or Format.R11G11B10Float => MTLVertexFormat.Float3, - Format.R16G16B16A16Float or Format.R32G32B32A32Float => MTLVertexFormat.Float4, - Format.R8Uint or Format.R16Uint or Format.R32Uint => MTLVertexFormat.UInt, - Format.R8G8Uint or Format.R16G16Uint or Format.R32G32Uint => MTLVertexFormat.UInt2, - Format.R8G8B8Uint or Format.R16G16B16Uint or Format.R32G32B32Uint => MTLVertexFormat.UInt3, - Format.R8G8B8A8Uint or Format.R16G16B16A16Uint or Format.R32G32B32A32Uint or Format.R10G10B10A2Uint => MTLVertexFormat.UInt4, - Format.R8Sint or Format.R16Sint or Format.R32Sint => MTLVertexFormat.Int, - Format.R8G8Sint or Format.R16G16Sint or Format.R32G32Sint => MTLVertexFormat.Int2, - Format.R8G8B8Sint or Format.R16G16B16Sint or Format.R32G32B32Sint => MTLVertexFormat.Int3, - Format.R8G8B8A8Sint or Format.R16G16B16A16Sint or Format.R32G32B32A32Sint => MTLVertexFormat.Int4, - Format.R8Unorm or Format.R16Unorm => MTLVertexFormat.UShortNormalized, - Format.R8G8Unorm or Format.R16G16Unorm => MTLVertexFormat.UShort2Normalized, - Format.R8G8B8Unorm or Format.R16G16B16Unorm => MTLVertexFormat.UShort3Normalized, - Format.R8G8B8A8Unorm or Format.R16G16B16A16Unorm or Format.R10G10B10A2Unorm => MTLVertexFormat.UShort4Normalized, - Format.R8Snorm or Format.R16Snorm => MTLVertexFormat.ShortNormalized, - Format.R8G8Snorm or Format.R16G16Snorm => MTLVertexFormat.Short2Normalized, - Format.R8G8B8Snorm or Format.R16G16B16Snorm => MTLVertexFormat.Short3Normalized, - Format.R8G8B8A8Snorm or Format.R16G16B16A16Snorm or Format.R10G10B10A2Snorm => MTLVertexFormat.Short4Normalized, + Format.R16Float => MTLVertexFormat.Half, + Format.R16G16Float => MTLVertexFormat.Half2, + Format.R16G16B16Float => MTLVertexFormat.Half3, + Format.R16G16B16A16Float => MTLVertexFormat.Half4, + Format.R32Float => MTLVertexFormat.Float, + Format.R32G32Float => MTLVertexFormat.Float2, + Format.R32G32B32Float=> MTLVertexFormat.Float3, + Format.R11G11B10Float => MTLVertexFormat.FloatRG11B10, + Format.R32G32B32A32Float => MTLVertexFormat.Float4, + Format.R8Uint => MTLVertexFormat.UChar, + Format.R8G8Uint => MTLVertexFormat.UChar2, + Format.R8G8B8Uint => MTLVertexFormat.UChar3, + Format.R8G8B8A8Uint => MTLVertexFormat.UChar4, + Format.R16Uint => MTLVertexFormat.UShort, + Format.R16G16Uint => MTLVertexFormat.UShort2, + Format.R16G16B16Uint => MTLVertexFormat.UShort3, + Format.R16G16B16A16Uint => MTLVertexFormat.UShort4, + Format.R32Uint => MTLVertexFormat.UInt, + Format.R32G32Uint => MTLVertexFormat.UInt2, + Format.R32G32B32Uint => MTLVertexFormat.UInt3, + Format.R32G32B32A32Uint => MTLVertexFormat.UInt4, + Format.R8Sint => MTLVertexFormat.Char, + Format.R8G8Sint => MTLVertexFormat.Char2, + Format.R8G8B8Sint => MTLVertexFormat.Char3, + Format.R8G8B8A8Sint => MTLVertexFormat.Char4, + Format.R16Sint => MTLVertexFormat.Short, + Format.R16G16Sint => MTLVertexFormat.Short2, + Format.R16G16B16Sint => MTLVertexFormat.Short3, + Format.R16G16B16A16Sint => MTLVertexFormat.Short4, + Format.R32Sint => MTLVertexFormat.Int, + Format.R32G32Sint => MTLVertexFormat.Int2, + Format.R32G32B32Sint => MTLVertexFormat.Int3, + Format.R32G32B32A32Sint => MTLVertexFormat.Int4, + Format.R8Unorm => MTLVertexFormat.UCharNormalized, + Format.R8G8Unorm => MTLVertexFormat.UChar2Normalized, + Format.R8G8B8Unorm => MTLVertexFormat.UChar3Normalized, + Format.R8G8B8A8Unorm => MTLVertexFormat.UChar4Normalized, + Format.R16Unorm => MTLVertexFormat.UShortNormalized, + Format.R16G16Unorm => MTLVertexFormat.UShort2Normalized, + Format.R16G16B16Unorm => MTLVertexFormat.UShort3Normalized, + Format.R16G16B16A16Unorm => MTLVertexFormat.UShort4Normalized, + Format.R10G10B10A2Unorm => MTLVertexFormat.UInt1010102Normalized, + Format.R8Snorm => MTLVertexFormat.CharNormalized, + Format.R8G8Snorm => MTLVertexFormat.Char2Normalized, + Format.R8G8B8Snorm => MTLVertexFormat.Char3Normalized, + Format.R8G8B8A8Snorm => MTLVertexFormat.Char4Normalized, + Format.R16Snorm => MTLVertexFormat.ShortNormalized, + Format.R16G16Snorm => MTLVertexFormat.Short2Normalized, + Format.R16G16B16Snorm => MTLVertexFormat.Short3Normalized, + Format.R16G16B16A16Snorm => MTLVertexFormat.Short4Normalized, + Format.R10G10B10A2Snorm => MTLVertexFormat.Int1010102Normalized, _ => LogInvalidAndReturn(format, nameof(Format), MTLVertexFormat.Float4) }; -- 2.47.1 From 9c5917912b92e82eca784e26b6a3e00163b39277 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 17:02:10 +0200 Subject: [PATCH 144/368] remove outdated comment --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index a3f90b7c3..a1e362d54 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -5,7 +5,6 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - // TODO: use this (unused right now) public struct DirtyFlags { public bool Pipeline = false; -- 2.47.1 From 96eea9de23129aef907173365425d3cbcc283532 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 19 May 2024 11:05:50 -0400 Subject: [PATCH 145/368] Cleanup --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 6 + .../EncoderStateManager.cs | 180 +++++++++--------- 2 files changed, 95 insertions(+), 91 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index a1e362d54..adbc568a9 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -16,6 +16,12 @@ namespace Ryujinx.Graphics.Metal Pipeline = true; DepthStencil = true; } + + public void Clear() + { + Pipeline = false; + DepthStencil = false; + } } [SupportedOSPlatform("macos")] diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index ad8714cf0..6e8314117 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -43,9 +43,7 @@ namespace Ryujinx.Graphics.Metal public MTLRenderCommandEncoder CreateRenderCommandEncoder() { // Initialise Pass & State - var renderPassDescriptor = new MTLRenderPassDescriptor(); - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); for (int i = 0; i < EncoderState.MaxColorAttachments; i++) { @@ -95,7 +93,6 @@ namespace Ryujinx.Graphics.Metal } // Initialise Encoder - var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); // Mark all state as dirty to ensure it is set on the encoder @@ -118,99 +115,103 @@ namespace Ryujinx.Graphics.Metal public void RebindState(MTLRenderCommandEncoder renderCommandEncoder) { - SetPipelineState(renderCommandEncoder); - SetDepthStencilState(renderCommandEncoder); + if (_currentState.Dirty.Pipeline) + { + SetPipelineState(renderCommandEncoder); + } - _currentState.Dirty = new(); + if (_currentState.Dirty.DepthStencil) + { + SetDepthStencilState(renderCommandEncoder); + } + + _currentState.Dirty.Clear(); } private void SetPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.Pipeline) + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + + for (int i = 0; i < EncoderState.MaxColorAttachments; i++) { - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - - for (int i = 0; i < EncoderState.MaxColorAttachments; i++) + if (_currentState.RenderTargets[i] != IntPtr.Zero) { - if (_currentState.RenderTargets[i] != IntPtr.Zero) - { - var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; - pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); + pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; + pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - if (_currentState.BlendDescriptors.TryGetValue(i, out BlendDescriptor blendDescriptor)) - { - pipelineAttachment.SetBlendingEnabled(blendDescriptor.Enable); - pipelineAttachment.AlphaBlendOperation = blendDescriptor.AlphaOp.Convert(); - pipelineAttachment.RgbBlendOperation = blendDescriptor.ColorOp.Convert(); - pipelineAttachment.SourceAlphaBlendFactor = blendDescriptor.AlphaSrcFactor.Convert(); - pipelineAttachment.DestinationAlphaBlendFactor = blendDescriptor.AlphaDstFactor.Convert(); - pipelineAttachment.SourceRGBBlendFactor = blendDescriptor.ColorSrcFactor.Convert(); - pipelineAttachment.DestinationRGBBlendFactor = blendDescriptor.ColorDstFactor.Convert(); - } + if (_currentState.BlendDescriptors.TryGetValue(i, out BlendDescriptor blendDescriptor)) + { + pipelineAttachment.SetBlendingEnabled(blendDescriptor.Enable); + pipelineAttachment.AlphaBlendOperation = blendDescriptor.AlphaOp.Convert(); + pipelineAttachment.RgbBlendOperation = blendDescriptor.ColorOp.Convert(); + pipelineAttachment.SourceAlphaBlendFactor = blendDescriptor.AlphaSrcFactor.Convert(); + pipelineAttachment.DestinationAlphaBlendFactor = blendDescriptor.AlphaDstFactor.Convert(); + pipelineAttachment.SourceRGBBlendFactor = blendDescriptor.ColorSrcFactor.Convert(); + pipelineAttachment.DestinationRGBBlendFactor = blendDescriptor.ColorDstFactor.Convert(); } } - - if (_currentState.DepthStencil != IntPtr.Zero) - { - switch (_currentState.DepthStencil.PixelFormat) - { - // Depth Only Attachment - case MTLPixelFormat.Depth16Unorm: - case MTLPixelFormat.Depth32Float: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; - - // Stencil Only Attachment - case MTLPixelFormat.Stencil8: - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; - - // Combined Attachment - case MTLPixelFormat.Depth24UnormStencil8: - case MTLPixelFormat.Depth32FloatStencil8: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - break; - default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); - break; - } - } - - renderPipelineDescriptor.VertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); - - if (_currentState.VertexFunction != null) - { - renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; - } - else - { - return; - } - - if (_currentState.FragmentFunction != null) - { - renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; - } - - var error = new NSError(IntPtr.Zero); - var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - renderCommandEncoder.SetRenderPipelineState(pipelineState); - - renderCommandEncoder.SetBlendColor( - _currentState.BlendColor.Red, - _currentState.BlendColor.Green, - _currentState.BlendColor.Blue, - _currentState.BlendColor.Alpha); } + + if (_currentState.DepthStencil != IntPtr.Zero) + { + switch (_currentState.DepthStencil.PixelFormat) + { + // Depth Only Attachment + case MTLPixelFormat.Depth16Unorm: + case MTLPixelFormat.Depth32Float: + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; + + // Stencil Only Attachment + case MTLPixelFormat.Stencil8: + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; + + // Combined Attachment + case MTLPixelFormat.Depth24UnormStencil8: + case MTLPixelFormat.Depth32FloatStencil8: + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + break; + default: + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); + break; + } + } + + renderPipelineDescriptor.VertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); + + if (_currentState.VertexFunction != null) + { + renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; + } + else + { + return; + } + + if (_currentState.FragmentFunction != null) + { + renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; + } + + var error = new NSError(IntPtr.Zero); + var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + renderCommandEncoder.SetRenderPipelineState(pipelineState); + + renderCommandEncoder.SetBlendColor( + _currentState.BlendColor.Red, + _currentState.BlendColor.Green, + _currentState.BlendColor.Blue, + _currentState.BlendColor.Alpha); } public void UpdateIndexBuffer(BufferRange buffer, IndexType type) @@ -551,12 +552,9 @@ namespace Ryujinx.Graphics.Metal private void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.DepthStencil) + if (_currentState.DepthStencilState != null) { - if (_currentState.DepthStencilState != null) - { - renderCommandEncoder.SetDepthStencilState(_currentState.DepthStencilState.Value); - } + renderCommandEncoder.SetDepthStencilState(_currentState.DepthStencilState.Value); } } -- 2.47.1 From 2316f30de1da3f44878432590540b59005ad13b1 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 19 May 2024 11:07:55 -0400 Subject: [PATCH 146/368] Use return value of BeginRenderPass --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index f1dcd19c0..91afb33bf 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -48,12 +48,16 @@ namespace Ryujinx.Graphics.Metal public MTLRenderCommandEncoder GetOrCreateRenderEncoder() { + MTLRenderCommandEncoder renderCommandEncoder; + if (_currentEncoder == null || _currentEncoderType != EncoderType.Render) { - BeginRenderPass(); + renderCommandEncoder = BeginRenderPass(); + } + else + { + renderCommandEncoder = new MTLRenderCommandEncoder(_currentEncoder.Value); } - - var renderCommandEncoder = new MTLRenderCommandEncoder(_currentEncoder.Value); _encoderStateManager.RebindState(renderCommandEncoder); -- 2.47.1 From dd5fb8bed93473d7b843bf5b139527e21ed809ac Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 17:28:00 +0200 Subject: [PATCH 147/368] implement pipeline cache --- src/Ryujinx.Graphics.Metal/Constants.cs | 5 + src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 +- .../EncoderStateManager.cs | 16 +- src/Ryujinx.Graphics.Metal/StateCache.cs | 178 ++++++++++++++++++ 4 files changed, 191 insertions(+), 12 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/StateCache.cs diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 21776ee58..f20598f9c 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -9,5 +9,10 @@ namespace Ryujinx.Graphics.Metal public const int MaxTexturesPerStage = 64; public const int MaxCommandBuffersPerQueue = 16; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; + public const int MaxColorAttachments = 8; + // TODO: Check this value + public const int MaxVertexAttributes = 16; + // TODO: Check this value + public const int MaxVertexLayouts = 16; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index adbc568a9..a787d1424 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -27,8 +27,6 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public struct EncoderState { - public const int MaxColorAttachments = 8; - public MTLFunction? VertexFunction = null; public MTLFunction? FragmentFunction = null; @@ -64,7 +62,7 @@ namespace Ryujinx.Graphics.Metal // Changes to attachments take recreation! public MTLTexture DepthStencil = default; - public MTLTexture[] RenderTargets = new MTLTexture[MaxColorAttachments]; + public MTLTexture[] RenderTargets = new MTLTexture[Constants.MaxColorAttachments]; public Dictionary BlendDescriptors = new(); public ColorF BlendColor = new(); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 6e8314117..15a96cc45 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Metal private readonly MTLDevice _device; private readonly Pipeline _pipeline; + private readonly RenderPipelineCache RenderPipelineCache; + private EncoderState _currentState = new(); private EncoderState _backState = new(); @@ -28,6 +30,7 @@ namespace Ryujinx.Graphics.Metal { _device = device; _pipeline = pipeline; + RenderPipelineCache = new(device); } public void SwapStates() @@ -45,7 +48,7 @@ namespace Ryujinx.Graphics.Metal // Initialise Pass & State var renderPassDescriptor = new MTLRenderPassDescriptor(); - for (int i = 0; i < EncoderState.MaxColorAttachments; i++) + for (int i = 0; i < Constants.MaxColorAttachments; i++) { if (_currentState.RenderTargets[i] != IntPtr.Zero) { @@ -131,7 +134,7 @@ namespace Ryujinx.Graphics.Metal private void SetPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - for (int i = 0; i < EncoderState.MaxColorAttachments; i++) + for (int i = 0; i < Constants.MaxColorAttachments; i++) { if (_currentState.RenderTargets[i] != IntPtr.Zero) { @@ -198,12 +201,7 @@ namespace Ryujinx.Graphics.Metal renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; } - var error = new NSError(IntPtr.Zero); - var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } + var pipelineState = RenderPipelineCache.GetOrCreate(renderPipelineDescriptor); renderCommandEncoder.SetRenderPipelineState(pipelineState); @@ -249,7 +247,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) { - _currentState.RenderTargets = new MTLTexture[EncoderState.MaxColorAttachments]; + _currentState.RenderTargets = new MTLTexture[Constants.MaxColorAttachments]; for (int i = 0; i < colors.Length; i++) { diff --git a/src/Ryujinx.Graphics.Metal/StateCache.cs b/src/Ryujinx.Graphics.Metal/StateCache.cs new file mode 100644 index 000000000..ce60c414b --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/StateCache.cs @@ -0,0 +1,178 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public abstract class StateCache + { + private Dictionary Cache = new(); + + protected abstract HashT GetHash(DescriptorT descriptor); + + protected abstract T CreateValue(DescriptorT descriptor); + + public T GetOrCreate(DescriptorT descriptor) + { + var hash = GetHash(descriptor); + if (Cache.TryGetValue(hash, out T value)) + { + return value; + } + else + { + var newValue = CreateValue(descriptor); + Cache.Add(hash, newValue); + + return newValue; + } + } + } + + [SupportedOSPlatform("macos")] + public struct RenderPipelineHash + { + public MTLFunction VertexFunction; + public MTLFunction FragmentFunction; + public struct ColorAttachmentHash + { + public MTLPixelFormat PixelFormat; + public bool BlendingEnabled; + public MTLBlendOperation RgbBlendOperation; + public MTLBlendOperation AlphaBlendOperation; + public MTLBlendFactor SourceRGBBlendFactor; + public MTLBlendFactor DestinationRGBBlendFactor; + public MTLBlendFactor SourceAlphaBlendFactor; + public MTLBlendFactor DestinationAlphaBlendFactor; + } + [System.Runtime.CompilerServices.InlineArray(Constants.MaxColorAttachments)] + public struct ColorAttachmentHashArray + { + public ColorAttachmentHash data; + } + public ColorAttachmentHashArray ColorAttachments; + public struct DepthStencilAttachmentHash + { + public MTLPixelFormat DepthPixelFormat; + public MTLPixelFormat StencilPixelFormat; + } + public DepthStencilAttachmentHash DepthStencilAttachment; + public struct VertexDescriptorHash + { + public struct AttributeHash + { + public MTLVertexFormat Format; + public int Offset; + public int BufferIndex; + } + [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexAttributes)] + public struct AttributeHashArray + { + public AttributeHash data; + } + public AttributeHashArray Attributes; + public struct LayoutHash + { + public MTLVertexFormat Format; + public int Stride; + public int StepFunction; + public int StepRate; + } + [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexLayouts)] + public struct LayoutHashArray + { + public LayoutHash data; + } + public LayoutHashArray Layouts; + } + public VertexDescriptorHash VertexDescriptor; + } + + [SupportedOSPlatform("macos")] + public class RenderPipelineCache : StateCache + { + private readonly MTLDevice _device; + + public RenderPipelineCache(MTLDevice device) { + _device = device; + } + + protected override RenderPipelineHash GetHash(MTLRenderPipelineDescriptor descriptor) { + var hash = new RenderPipelineHash(); + + // Functions + hash.VertexFunction = descriptor.VertexFunction; + hash.FragmentFunction = descriptor.FragmentFunction; + + // Color Attachments + for (int i = 0; i < Constants.MaxColorAttachments; i++) + { + var attachment = descriptor.ColorAttachments.Object((ulong)i); + hash.ColorAttachments[i] = new RenderPipelineHash.ColorAttachmentHash + { + PixelFormat = attachment.PixelFormat, + BlendingEnabled = attachment.BlendingEnabled, + RgbBlendOperation = attachment.RgbBlendOperation, + AlphaBlendOperation = attachment.AlphaBlendOperation, + SourceRGBBlendFactor = attachment.SourceRGBBlendFactor, + DestinationRGBBlendFactor = attachment.DestinationRGBBlendFactor, + SourceAlphaBlendFactor = attachment.SourceAlphaBlendFactor, + DestinationAlphaBlendFactor = attachment.DestinationAlphaBlendFactor + }; + } + + // Depth stencil attachment + hash.DepthStencilAttachment = new RenderPipelineHash.DepthStencilAttachmentHash + { + DepthPixelFormat = descriptor.DepthAttachmentPixelFormat, + StencilPixelFormat = descriptor.StencilAttachmentPixelFormat + }; + + // Vertex descriptor + hash.VertexDescriptor = new RenderPipelineHash.VertexDescriptorHash(); + + // Attributes + for (int i = 0; i < Constants.MaxVertexAttributes; i++) + { + var attribute = descriptor.VertexDescriptor.Attributes.Object((ulong)i); + hash.VertexDescriptor.Attributes[i] = new RenderPipelineHash.VertexDescriptorHash.AttributeHash + { + Format = attribute.Format, + Offset = (int)attribute.Offset, + BufferIndex = (int)attribute.BufferIndex + }; + } + + // Layouts + for (int i = 0; i < Constants.MaxVertexLayouts; i++) + { + var layout = descriptor.VertexDescriptor.Layouts.Object((ulong)i); + hash.VertexDescriptor.Layouts[i] = new RenderPipelineHash.VertexDescriptorHash.LayoutHash + { + Stride = (int)layout.Stride, + StepFunction = (int)layout.StepFunction, + StepRate = (int)layout.StepRate + }; + } + + return hash; + } + + protected override MTLRenderPipelineState CreateValue(MTLRenderPipelineDescriptor descriptor) + { + var error = new NSError(IntPtr.Zero); + var pipelineState = _device.NewRenderPipelineState(descriptor, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + return pipelineState; + } + } +} -- 2.47.1 From 5d0ae23a0b25b0bd2ca1df2e80e309d5f716cc7b Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 17:31:24 +0200 Subject: [PATCH 148/368] put render pipeline cache into a separate file --- .../RenderPipelineCache.cs | 152 ++++++++++++++++++ src/Ryujinx.Graphics.Metal/StateCache.cs | 142 ---------------- 2 files changed, 152 insertions(+), 142 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs diff --git a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs new file mode 100644 index 000000000..1da672134 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs @@ -0,0 +1,152 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public struct RenderPipelineHash + { + public MTLFunction VertexFunction; + public MTLFunction FragmentFunction; + public struct ColorAttachmentHash + { + public MTLPixelFormat PixelFormat; + public bool BlendingEnabled; + public MTLBlendOperation RgbBlendOperation; + public MTLBlendOperation AlphaBlendOperation; + public MTLBlendFactor SourceRGBBlendFactor; + public MTLBlendFactor DestinationRGBBlendFactor; + public MTLBlendFactor SourceAlphaBlendFactor; + public MTLBlendFactor DestinationAlphaBlendFactor; + } + [System.Runtime.CompilerServices.InlineArray(Constants.MaxColorAttachments)] + public struct ColorAttachmentHashArray + { + public ColorAttachmentHash data; + } + public ColorAttachmentHashArray ColorAttachments; + public struct DepthStencilAttachmentHash + { + public MTLPixelFormat DepthPixelFormat; + public MTLPixelFormat StencilPixelFormat; + } + public DepthStencilAttachmentHash DepthStencilAttachment; + public struct VertexDescriptorHash + { + public struct AttributeHash + { + public MTLVertexFormat Format; + public int Offset; + public int BufferIndex; + } + [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexAttributes)] + public struct AttributeHashArray + { + public AttributeHash data; + } + public AttributeHashArray Attributes; + public struct LayoutHash + { + public MTLVertexFormat Format; + public int Stride; + public int StepFunction; + public int StepRate; + } + [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexLayouts)] + public struct LayoutHashArray + { + public LayoutHash data; + } + public LayoutHashArray Layouts; + } + public VertexDescriptorHash VertexDescriptor; + } + + [SupportedOSPlatform("macos")] + public class RenderPipelineCache : StateCache + { + private readonly MTLDevice _device; + + public RenderPipelineCache(MTLDevice device) { + _device = device; + } + + protected override RenderPipelineHash GetHash(MTLRenderPipelineDescriptor descriptor) { + var hash = new RenderPipelineHash(); + + // Functions + hash.VertexFunction = descriptor.VertexFunction; + hash.FragmentFunction = descriptor.FragmentFunction; + + // Color Attachments + for (int i = 0; i < Constants.MaxColorAttachments; i++) + { + var attachment = descriptor.ColorAttachments.Object((ulong)i); + hash.ColorAttachments[i] = new RenderPipelineHash.ColorAttachmentHash + { + PixelFormat = attachment.PixelFormat, + BlendingEnabled = attachment.BlendingEnabled, + RgbBlendOperation = attachment.RgbBlendOperation, + AlphaBlendOperation = attachment.AlphaBlendOperation, + SourceRGBBlendFactor = attachment.SourceRGBBlendFactor, + DestinationRGBBlendFactor = attachment.DestinationRGBBlendFactor, + SourceAlphaBlendFactor = attachment.SourceAlphaBlendFactor, + DestinationAlphaBlendFactor = attachment.DestinationAlphaBlendFactor + }; + } + + // Depth stencil attachment + hash.DepthStencilAttachment = new RenderPipelineHash.DepthStencilAttachmentHash + { + DepthPixelFormat = descriptor.DepthAttachmentPixelFormat, + StencilPixelFormat = descriptor.StencilAttachmentPixelFormat + }; + + // Vertex descriptor + hash.VertexDescriptor = new RenderPipelineHash.VertexDescriptorHash(); + + // Attributes + for (int i = 0; i < Constants.MaxVertexAttributes; i++) + { + var attribute = descriptor.VertexDescriptor.Attributes.Object((ulong)i); + hash.VertexDescriptor.Attributes[i] = new RenderPipelineHash.VertexDescriptorHash.AttributeHash + { + Format = attribute.Format, + Offset = (int)attribute.Offset, + BufferIndex = (int)attribute.BufferIndex + }; + } + + // Layouts + for (int i = 0; i < Constants.MaxVertexLayouts; i++) + { + var layout = descriptor.VertexDescriptor.Layouts.Object((ulong)i); + hash.VertexDescriptor.Layouts[i] = new RenderPipelineHash.VertexDescriptorHash.LayoutHash + { + Stride = (int)layout.Stride, + StepFunction = (int)layout.StepFunction, + StepRate = (int)layout.StepRate + }; + } + + return hash; + } + + protected override MTLRenderPipelineState CreateValue(MTLRenderPipelineDescriptor descriptor) + { + var error = new NSError(IntPtr.Zero); + var pipelineState = _device.NewRenderPipelineState(descriptor, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + return pipelineState; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/StateCache.cs b/src/Ryujinx.Graphics.Metal/StateCache.cs index ce60c414b..66fefcf7b 100644 --- a/src/Ryujinx.Graphics.Metal/StateCache.cs +++ b/src/Ryujinx.Graphics.Metal/StateCache.cs @@ -33,146 +33,4 @@ namespace Ryujinx.Graphics.Metal } } } - - [SupportedOSPlatform("macos")] - public struct RenderPipelineHash - { - public MTLFunction VertexFunction; - public MTLFunction FragmentFunction; - public struct ColorAttachmentHash - { - public MTLPixelFormat PixelFormat; - public bool BlendingEnabled; - public MTLBlendOperation RgbBlendOperation; - public MTLBlendOperation AlphaBlendOperation; - public MTLBlendFactor SourceRGBBlendFactor; - public MTLBlendFactor DestinationRGBBlendFactor; - public MTLBlendFactor SourceAlphaBlendFactor; - public MTLBlendFactor DestinationAlphaBlendFactor; - } - [System.Runtime.CompilerServices.InlineArray(Constants.MaxColorAttachments)] - public struct ColorAttachmentHashArray - { - public ColorAttachmentHash data; - } - public ColorAttachmentHashArray ColorAttachments; - public struct DepthStencilAttachmentHash - { - public MTLPixelFormat DepthPixelFormat; - public MTLPixelFormat StencilPixelFormat; - } - public DepthStencilAttachmentHash DepthStencilAttachment; - public struct VertexDescriptorHash - { - public struct AttributeHash - { - public MTLVertexFormat Format; - public int Offset; - public int BufferIndex; - } - [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexAttributes)] - public struct AttributeHashArray - { - public AttributeHash data; - } - public AttributeHashArray Attributes; - public struct LayoutHash - { - public MTLVertexFormat Format; - public int Stride; - public int StepFunction; - public int StepRate; - } - [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexLayouts)] - public struct LayoutHashArray - { - public LayoutHash data; - } - public LayoutHashArray Layouts; - } - public VertexDescriptorHash VertexDescriptor; - } - - [SupportedOSPlatform("macos")] - public class RenderPipelineCache : StateCache - { - private readonly MTLDevice _device; - - public RenderPipelineCache(MTLDevice device) { - _device = device; - } - - protected override RenderPipelineHash GetHash(MTLRenderPipelineDescriptor descriptor) { - var hash = new RenderPipelineHash(); - - // Functions - hash.VertexFunction = descriptor.VertexFunction; - hash.FragmentFunction = descriptor.FragmentFunction; - - // Color Attachments - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - var attachment = descriptor.ColorAttachments.Object((ulong)i); - hash.ColorAttachments[i] = new RenderPipelineHash.ColorAttachmentHash - { - PixelFormat = attachment.PixelFormat, - BlendingEnabled = attachment.BlendingEnabled, - RgbBlendOperation = attachment.RgbBlendOperation, - AlphaBlendOperation = attachment.AlphaBlendOperation, - SourceRGBBlendFactor = attachment.SourceRGBBlendFactor, - DestinationRGBBlendFactor = attachment.DestinationRGBBlendFactor, - SourceAlphaBlendFactor = attachment.SourceAlphaBlendFactor, - DestinationAlphaBlendFactor = attachment.DestinationAlphaBlendFactor - }; - } - - // Depth stencil attachment - hash.DepthStencilAttachment = new RenderPipelineHash.DepthStencilAttachmentHash - { - DepthPixelFormat = descriptor.DepthAttachmentPixelFormat, - StencilPixelFormat = descriptor.StencilAttachmentPixelFormat - }; - - // Vertex descriptor - hash.VertexDescriptor = new RenderPipelineHash.VertexDescriptorHash(); - - // Attributes - for (int i = 0; i < Constants.MaxVertexAttributes; i++) - { - var attribute = descriptor.VertexDescriptor.Attributes.Object((ulong)i); - hash.VertexDescriptor.Attributes[i] = new RenderPipelineHash.VertexDescriptorHash.AttributeHash - { - Format = attribute.Format, - Offset = (int)attribute.Offset, - BufferIndex = (int)attribute.BufferIndex - }; - } - - // Layouts - for (int i = 0; i < Constants.MaxVertexLayouts; i++) - { - var layout = descriptor.VertexDescriptor.Layouts.Object((ulong)i); - hash.VertexDescriptor.Layouts[i] = new RenderPipelineHash.VertexDescriptorHash.LayoutHash - { - Stride = (int)layout.Stride, - StepFunction = (int)layout.StepFunction, - StepRate = (int)layout.StepRate - }; - } - - return hash; - } - - protected override MTLRenderPipelineState CreateValue(MTLRenderPipelineDescriptor descriptor) - { - var error = new NSError(IntPtr.Zero); - var pipelineState = _device.NewRenderPipelineState(descriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - return pipelineState; - } - } } -- 2.47.1 From 913f25b2a065349458e591da20a65b29e1a2c8ff Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 18:38:08 +0200 Subject: [PATCH 149/368] support multiple render targets & fix: incorrect texture name --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 2 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 6 ++++-- .../CodeGen/Msl/Instructions/IoMap.cs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 4f6015ee7..657579a24 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -197,7 +197,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { IoVariable.Position => "position", IoVariable.PointSize => "point_size", - IoVariable.FragmentOutputColor => "color", + IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", _ => $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 217c21816..44051a3ad 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -267,7 +267,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return NumberFormatter.FormatInt(0); } - string textureName = "texture"; + string samplerName = GetSamplerName(context.Properties, texOp); + string textureName = $"tex_{samplerName}"; string texCall = textureName + "."; texCall += $"get_num_samples()"; @@ -278,7 +279,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { AstTextureOperation texOp = (AstTextureOperation)operation; - string textureName = "texture"; + string samplerName = GetSamplerName(context.Properties, texOp); + string textureName = $"tex_{samplerName}"; string texCall = textureName + "."; if (texOp.Index == 3) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 9ead6bc56..c836d9832 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.BaseInstance => ("base_instance", AggregateType.S32), IoVariable.BaseVertex => ("base_vertex", AggregateType.S32), IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), - IoVariable.FragmentOutputColor => ("out.color", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.FragmentOutputColor => ($"out.color{location}", AggregateType.Vector4 | AggregateType.FP32), IoVariable.FragmentOutputDepth => ("depth", AggregateType.FP32), IoVariable.FrontFacing => ("front_facing", AggregateType.Bool), IoVariable.InstanceId => ("instance_id", AggregateType.S32), -- 2.47.1 From d5b98d618732cc12b37e9ddcd67308a72a3cbcb6 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 19:07:27 +0200 Subject: [PATCH 150/368] declare local memory --- src/Ryujinx.Graphics.Metal/Program.cs | 2 ++ .../CodeGen/Msl/Declarations.cs | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 764bcf126..f6f5720e5 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Metal { Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); _status = ProgramLinkStatus.Failure; + Console.WriteLine(shader.Code); + throw new NotImplementedException(); return; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 657579a24..42a756233 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -63,6 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public static void DeclareLocals(CodeGenContext context, StructuredFunction function, ShaderStage stage) { + DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); switch (stage) { case ShaderStage.Vertex: @@ -106,6 +107,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl }; } + private static void DeclareMemories(CodeGenContext context, IEnumerable memories, bool isShared) + { + foreach (var memory in memories) + { + var typeName = GetVarTypeName(context, memory.Type & ~AggregateType.Array); + context.AppendLine($"{typeName} {memory.Name}[{memory.ArrayLength}];"); + } + } + private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) { if (context.Definitions.IaIndexing) -- 2.47.1 From 765ca8e6c00e78c15d086b4edc774fecd5c98bee Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 19:12:17 +0200 Subject: [PATCH 151/368] don't use mask on size query --- src/Ryujinx.Graphics.Metal/Program.cs | 2 -- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index f6f5720e5..764bcf126 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -28,8 +28,6 @@ namespace Ryujinx.Graphics.Metal { Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); _status = ProgramLinkStatus.Failure; - Console.WriteLine(shader.Code); - throw new NotImplementedException(); return; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 44051a3ad..c1eeca7c8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -316,7 +316,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCall += $"{lodExpr}"; } - texCall += $"){GetMask(texOp.Index)}"; + texCall += $")"; } return texCall; -- 2.47.1 From 7a6c7196c820bec6fc31531e096e870244fb92bf Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 21 May 2024 16:23:42 +0200 Subject: [PATCH 152/368] fix: incorrect layer count of texture view --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- src/Ryujinx.Graphics.Metal/Texture.cs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 4cd230dbc..e7d26f72a 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Metal public void BackgroundContextAction(Action action, bool alwaysBackground = false) { - throw new NotImplementedException(); + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 63e51305e..a50d416aa 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -68,11 +68,7 @@ namespace Ryujinx.Graphics.Metal slices.location = (ulong)firstLayer; slices.length = 1; - if (info.Target == Target.Texture3D) - { - slices.length = (ulong)Info.Depth; - } - else if (info.Target != Target.Cubemap) + if (info.Target != Target.Texture3D && info.Target != Target.Cubemap) { slices.length = (ulong)Info.Depth; } -- 2.47.1 From 27ece39dbe3259d7c25c9f053e628749d69c5c9e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 15:02:21 -0400 Subject: [PATCH 153/368] More shader fixes --- .../CodeGen/Msl/Declarations.cs | 46 ++++++++++++------- .../CodeGen/Msl/Instructions/IoMap.cs | 15 +++++- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 42a756233..e87faef78 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -143,24 +143,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.EnterScope(); - if (context.Definitions.Stage == ShaderStage.Fragment) - { - // TODO: check if it's needed - context.AppendLine("float4 position [[position]];"); - } - foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { - string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)); - string name = $"{DefaultNames.IAttributePrefix}{ioDefinition.Location}"; - string suffix = context.Definitions.Stage switch + string type = ioDefinition.IoVariable switch { - ShaderStage.Vertex => $" [[attribute({ioDefinition.Location})]]", - ShaderStage.Fragment => $" [[user(loc{ioDefinition.Location})]]", + IoVariable.Position => "float4", + IoVariable.GlobalId => "uint3", + IoVariable.VertexId => "uint", + IoVariable.VertexIndex => "uint", + _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) + }; + string name = ioDefinition.IoVariable switch + { + IoVariable.Position => "position", + IoVariable.GlobalId => "global_id", + IoVariable.VertexId => "vertex_id", + IoVariable.VertexIndex => "vertex_index", + _ => $"{DefaultNames.IAttributePrefix}{ioDefinition.Location}" + }; + string suffix = ioDefinition.IoVariable switch + { + IoVariable.Position => "[[position]]", + IoVariable.GlobalId => "[[thread_position_in_grid]]", + IoVariable.VertexId => "[[vertex_id]]", + // TODO: Avoid potential redeclaration + IoVariable.VertexIndex => "[[vertex_id]]", + IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]", _ => "" }; - context.AppendLine($"{type} {name}{suffix};"); + context.AppendLine($"{type} {name} {suffix};"); } context.LeaveScope(";"); @@ -212,14 +224,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl }; string suffix = ioDefinition.IoVariable switch { - IoVariable.Position => " [[position]]", - IoVariable.PointSize => " [[point_size]]", - IoVariable.UserDefined => $" [[user(loc{ioDefinition.Location})]]", - IoVariable.FragmentOutputColor => $" [[color({ioDefinition.Location})]]", + IoVariable.Position => "[[position]]", + IoVariable.PointSize => "[[point_size]]", + IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", + IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", _ => "" }; - context.AppendLine($"{type} {name}{suffix};"); + context.AppendLine($"{type} {name} {suffix};"); } context.LeaveScope(";"); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index c836d9832..4eb4f2581 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System.Globalization; @@ -14,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions bool isOutput, bool isPerPatch) { - return ioVariable switch + var returnValue = ioVariable switch { IoVariable.BaseInstance => ("base_instance", AggregateType.S32), IoVariable.BaseVertex => ("base_vertex", AggregateType.S32), @@ -29,10 +30,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), IoVariable.VertexId => ("vertex_id", AggregateType.S32), + IoVariable.GlobalId => ("global_id", AggregateType.Vector3 | AggregateType.U32), + // gl_VertexIndex does not have a direct equivalent in MSL + IoVariable.VertexIndex => ("vertex_index", AggregateType.U32), IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), - IoVariable.FragmentCoord => ("in.position", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.FragmentCoord => ("position", AggregateType.Vector4 | AggregateType.FP32), _ => (null, AggregateType.Invalid), }; + + if (returnValue.Item2 == AggregateType.Invalid) + { + Logger.Warning?.PrintMsg(LogClass.Gpu, $"Unable to find type for IoVariable {ioVariable}!"); + } + + return returnValue; } private static (string, AggregateType) GetUserDefinedVariableName(ShaderDefinitions definitions, int location, int component, bool isOutput, bool isPerPatch) -- 2.47.1 From 38385bad30e4cdad7b1244d7a6b995621f927315 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 15:04:44 -0400 Subject: [PATCH 154/368] Nvm it should be in.position --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 4eb4f2581..edc907712 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions // gl_VertexIndex does not have a direct equivalent in MSL IoVariable.VertexIndex => ("vertex_index", AggregateType.U32), IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), - IoVariable.FragmentCoord => ("position", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.FragmentCoord => ("in.position", AggregateType.Vector4 | AggregateType.FP32), _ => (null, AggregateType.Invalid), }; -- 2.47.1 From 486fd78eba3b698027a9caf7ca984361b5b91717 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 15:07:12 -0400 Subject: [PATCH 155/368] Revert position changes --- .../CodeGen/Msl/Declarations.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index e87faef78..c2d5e5976 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -143,11 +143,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.EnterScope(); + if (context.Definitions.Stage == ShaderStage.Fragment) + { + // TODO: check if it's needed + context.AppendLine("float4 position [[position]];"); + } + + foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { string type = ioDefinition.IoVariable switch { - IoVariable.Position => "float4", + // IoVariable.Position => "float4", IoVariable.GlobalId => "uint3", IoVariable.VertexId => "uint", IoVariable.VertexIndex => "uint", @@ -155,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl }; string name = ioDefinition.IoVariable switch { - IoVariable.Position => "position", + // IoVariable.Position => "position", IoVariable.GlobalId => "global_id", IoVariable.VertexId => "vertex_id", IoVariable.VertexIndex => "vertex_index", @@ -163,7 +170,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl }; string suffix = ioDefinition.IoVariable switch { - IoVariable.Position => "[[position]]", + // IoVariable.Position => "[[position]]", IoVariable.GlobalId => "[[thread_position_in_grid]]", IoVariable.VertexId => "[[vertex_id]]", // TODO: Avoid potential redeclaration -- 2.47.1 From b76f9105c80a229632409752f911a904fa2e3211 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 15:44:00 -0400 Subject: [PATCH 156/368] Depth Sampler Fixes --- .../CodeGen/Msl/Declarations.cs | 1 - .../CodeGen/Msl/Instructions/InstGenMemory.cs | 26 +++++++++++----- src/Ryujinx.Graphics.Shader/SamplerType.cs | 31 +++++++++++++------ 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index c2d5e5976..deb5dc394 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -149,7 +149,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine("float4 position [[position]];"); } - foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { string type = ioDefinition.IoVariable switch diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index c1eeca7c8..b771ec12e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -175,20 +175,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } else { - texCall += "sample("; + texCall += "sample"; - texCall += $"samp_{samplerName}"; + if (isGather) + { + texCall += "_gather"; + } + + if (isShadow) + { + texCall += "_compare"; + } + + texCall += $"(samp_{samplerName}"; } int coordsCount = texOp.Type.GetDimensions(); int pCount = coordsCount; - if (isShadow && !isGather) - { - pCount++; - } - void Append(string str) { texCall += ", " + str; @@ -224,6 +229,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCall += ", " + Src(AggregateType.S32); } + if (isShadow) + { + texCall += ", " + Src(AggregateType.S32); + } + + // TODO: Support offsets + texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); return texCall; diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index f9ae96661..de43d7aa6 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -158,16 +158,29 @@ namespace Ryujinx.Graphics.Shader public static string ToMslTextureType(this SamplerType type) { - string typeName = (type & SamplerType.Mask) switch + string typeName; + + if ((type & SamplerType.Shadow) != 0) { - SamplerType.None => "texture", - SamplerType.Texture1D => "texture1d", - SamplerType.TextureBuffer => "texturebuffer", - SamplerType.Texture2D => "texture2d", - SamplerType.Texture3D => "texture3d", - SamplerType.TextureCube => "texturecube", - _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), - }; + typeName = (type & SamplerType.Mask) switch + { + SamplerType.Texture2D => "depth2d", + SamplerType.TextureCube => "depthcube", + _ => throw new ArgumentException($"Invalid shadow texture type \"{type}\"."), + }; + } + else + { + typeName = (type & SamplerType.Mask) switch + { + SamplerType.Texture1D => "texture1d", + SamplerType.TextureBuffer => "texturebuffer", + SamplerType.Texture2D => "texture2d", + SamplerType.Texture3D => "texture3d", + SamplerType.TextureCube => "texturecube", + _ => throw new ArgumentException($"Invalid texture type \"{type}\"."), + }; + } if ((type & SamplerType.Multisample) != 0) { -- 2.47.1 From 381f4ec091ff489aed4adbceec79dc7c60ef7dcd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 20:27:37 -0400 Subject: [PATCH 157/368] FragmentOutputDepth Fixes --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 3 +++ src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index deb5dc394..72f60c04f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -219,6 +219,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { IoVariable.Position => "float4", IoVariable.PointSize => "float", + IoVariable.FragmentOutputDepth => "float", _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) }; string name = ioDefinition.IoVariable switch @@ -226,6 +227,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.Position => "position", IoVariable.PointSize => "point_size", IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", + IoVariable.FragmentOutputDepth => "depth", _ => $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch @@ -234,6 +236,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.PointSize => "[[point_size]]", IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", + IoVariable.FragmentOutputDepth => "[[depth(any)]]", _ => "" }; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index edc907712..d0c198904 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.BaseVertex => ("base_vertex", AggregateType.S32), IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), IoVariable.FragmentOutputColor => ($"out.color{location}", AggregateType.Vector4 | AggregateType.FP32), - IoVariable.FragmentOutputDepth => ("depth", AggregateType.FP32), + IoVariable.FragmentOutputDepth => ("out.depth", AggregateType.FP32), IoVariable.FrontFacing => ("front_facing", AggregateType.Bool), IoVariable.InstanceId => ("instance_id", AggregateType.S32), IoVariable.PointCoord => ("point_coord", AggregateType.Vector2), -- 2.47.1 From 90e3899c23c495dc362e03af07166542c09a4033 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 17:21:44 -0400 Subject: [PATCH 158/368] Shitty Clears + Inline Buffer Improvements? --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 +- .../EncoderStateManager.cs | 13 ++-- src/Ryujinx.Graphics.Metal/HelperShader.cs | 68 ++++++++----------- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 22 +++--- src/Ryujinx.Graphics.Metal/Pipeline.cs | 9 ++- .../Ryujinx.Graphics.Metal.csproj | 4 +- .../Shaders/ColorClear.metal | 28 ++++++++ .../Shaders/ColorClearF.metal | 0 .../Shaders/ColorClearSI.metal | 0 .../Shaders/ColorClearUI.metal | 0 .../Shaders/DepthStencilClear.metal | 37 ++++++++++ 11 files changed, 124 insertions(+), 61 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index a787d1424..3ebf9fbd9 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - public struct EncoderState + struct EncoderState { public MTLFunction? VertexFunction = null; public MTLFunction? FragmentFunction = null; @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Metal // Changes to attachments take recreation! public MTLTexture DepthStencil = default; - public MTLTexture[] RenderTargets = new MTLTexture[Constants.MaxColorAttachments]; + public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments]; public Dictionary BlendDescriptors = new(); public ColorF BlendColor = new(); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 15a96cc45..ae4be9e34 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -25,6 +25,7 @@ namespace Ryujinx.Graphics.Metal public readonly MTLIndexType IndexType => _currentState.IndexType; public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; public readonly PrimitiveTopology Topology => _currentState.Topology; + public readonly Texture[] RenderTargets => _currentState.RenderTargets; public EncoderStateManager(MTLDevice device, Pipeline pipeline) { @@ -50,10 +51,10 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < Constants.MaxColorAttachments; i++) { - if (_currentState.RenderTargets[i] != IntPtr.Zero) + if (_currentState.RenderTargets[i] != null) { var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); - passAttachment.Texture = _currentState.RenderTargets[i]; + passAttachment.Texture = _currentState.RenderTargets[i].MTLTexture; passAttachment.LoadAction = MTLLoadAction.Load; } } @@ -136,10 +137,10 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < Constants.MaxColorAttachments; i++) { - if (_currentState.RenderTargets[i] != IntPtr.Zero) + if (_currentState.RenderTargets[i] != null) { var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; + pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].MTLTexture.PixelFormat; pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; @@ -247,7 +248,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) { - _currentState.RenderTargets = new MTLTexture[Constants.MaxColorAttachments]; + _currentState.RenderTargets = new Texture[Constants.MaxColorAttachments]; for (int i = 0; i < colors.Length; i++) { @@ -256,7 +257,7 @@ namespace Ryujinx.Graphics.Metal continue; } - _currentState.RenderTargets[i] = tex.MTLTexture; + _currentState.RenderTargets[i] = tex; } if (depthStencil is Texture depthTexture) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index b4ddfe02c..8e95442cd 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -2,8 +2,10 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; +using SharpMetal.Foundation; using SharpMetal.Metal; using System; +using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -23,9 +25,7 @@ namespace Ryujinx.Graphics.Metal private MTLDevice _device; private readonly IProgram _programColorBlit; - private readonly IProgram _programColorClearF; - private readonly IProgram _programColorClearSI; - private readonly IProgram _programColorClearUI; + private readonly IProgram _programColorClear; private readonly IProgram _programDepthStencilClear; public HelperShader(MTLDevice device, Pipeline pipeline) @@ -40,6 +40,13 @@ namespace Ryujinx.Graphics.Metal new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) ], device); + var colorClearSource = ReadMsl("ColorClear.metal"); + _programColorClear = new Program( + [ + new ShaderSource(colorClearSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(colorClearSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], device); + // var colorClearFSource = ReadMsl("ColorClearF.metal"); // _programColorClearF = new Program( // [ @@ -93,45 +100,30 @@ namespace Ryujinx.Graphics.Metal _pipeline.Finish(); } - public void ClearColor( + public unsafe void ClearColor( Texture dst, - uint componentMask, - int dstWidth, - int dstHeight, - ComponentType type, - Rectangle scissor) + ReadOnlySpan clearColor) { - Span viewports = stackalloc Viewport[1]; + const int ClearColorBufferSize = 16; - viewports[0] = new Viewport( - new Rectangle(0, 0, dstWidth, dstHeight), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); + var buffer = _device.NewBuffer(ClearColorBufferSize, MTLResourceOptions.ResourceStorageModeManaged); + var span = new Span(buffer.Contents.ToPointer(), ClearColorBufferSize); + clearColor.CopyTo(span); - IProgram program; - - if (type == ComponentType.SignedInteger) + buffer.DidModifyRange(new NSRange { - program = _programColorClearSI; - } - else if (type == ComponentType.UnsignedInteger) - { - program = _programColorClearUI; - } - else - { - program = _programColorClearF; - } + location = 0, + length = ClearColorBufferSize + }); - _pipeline.SetProgram(program); - // _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight); - _pipeline.SetRenderTargetColorMasks([componentMask]); - _pipeline.SetViewports(viewports); - _pipeline.SetScissors([scissor]); + var handle = buffer.NativePtr; + var range = new BufferRange(Unsafe.As(ref handle), 0, ClearColorBufferSize); + + _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); + + _pipeline.SetProgram(_programColorClear); + _pipeline.SetRenderTargets([dst], null); + // _pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); _pipeline.Finish(); @@ -196,9 +188,7 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { _programColorBlit.Dispose(); - _programColorClearF.Dispose(); - _programColorClearSI.Dispose(); - _programColorClearUI.Dispose(); + _programColorClear.Dispose(); _programDepthStencilClear.Dispose(); _pipeline.Dispose(); } diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index e7d26f72a..b5496028c 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -213,17 +213,19 @@ namespace Ryujinx.Graphics.Metal public unsafe void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) { - MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); - var span = new Span(mtlBuffer.Contents.ToPointer(), (int)mtlBuffer.Length); - data.CopyTo(span[offset..]); - if (mtlBuffer.StorageMode == MTLStorageMode.Managed) + var blitEncoder = _pipeline.GetOrCreateBlitEncoder(); + + MTLBuffer src = _device.NewBuffer((ulong)data.Length, MTLResourceOptions.ResourceStorageModeManaged); + var span = new Span(src.Contents.ToPointer(), data.Length); + data.CopyTo(span); + src.DidModifyRange(new NSRange { - mtlBuffer.DidModifyRange(new NSRange - { - location = (ulong)offset, - length = (ulong)data.Length - }); - } + location = 0, + length = (ulong)data.Length + }); + + MTLBuffer dst = new(Unsafe.As(ref buffer)); + blitEncoder.CopyFromBuffer(src, 0, dst, (ulong)offset, (ulong)data.Length); } public void UpdateCounters() diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 91afb33bf..ce9686b08 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -5,6 +5,7 @@ using SharpMetal.Foundation; using SharpMetal.Metal; using SharpMetal.QuartzCore; using System; +using System.Drawing; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -203,7 +204,13 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; + + Texture target = _encoderStateManager.RenderTargets[index]; + + _encoderStateManager.SwapStates(); + + _helperShader.ClearColor(target, colors); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index d8da12834..0824accc1 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -16,9 +16,7 @@ - - - + diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal new file mode 100644 index 000000000..85ae873e6 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal @@ -0,0 +1,28 @@ +#include + +using namespace metal; + +struct VertexOut { + float4 position [[position]]; +}; + +vertex VertexOut vertexMain(ushort vid [[vertex_id]]) +{ + int low = vid & 1; + int high = vid >> 1; + + VertexOut out; + + out.position.x = (float(low) - 0.5f) * 2.0f; + out.position.y = (float(high) - 0.5f) * 2.0f; + out.position.z = 0.0f; + out.position.w = 1.0f; + + return out; +} + +fragment float4 fragmentMain(VertexOut in [[stage_in]], + constant float4& clear_color [[buffer(0)]]) +{ + return clear_color; +} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal index e69de29bb..0a4e10a25 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal @@ -0,0 +1,37 @@ +#include + +using namespace metal; + +struct VertexOut { + float4 position [[position]]; +}; + +struct FragmentOut { + float4 color [[color(0)]]; + float depth [[depth(any)]]; +}; + +vertex VertexOut vertexMain(ushort vid [[vertex_id]]) +{ + int low = vid & 1; + int high = vid >> 1; + + VertexOut out; + + out.position.x = (float(low) - 0.5f) * 2.0f; + out.position.y = (float(high) - 0.5f) * 2.0f; + out.position.z = 0.0f; + out.position.w = 1.0f; + + return out; +} + +fragment float4 fragmentMain(VertexOut in [[stage_in]], + constant float clear_color [[buffer(0)]]) +{ + Fragment out; + + out.depth = clear_color; + + return out; +} -- 2.47.1 From 7f8d54d6dccff3687d5a4eed451dc551e0aadec7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 20:26:54 -0400 Subject: [PATCH 159/368] Depth Clear --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 2 +- .../EncoderStateManager.cs | 33 ++++----- src/Ryujinx.Graphics.Metal/HelperShader.cs | 72 +++++++------------ src/Ryujinx.Graphics.Metal/Pipeline.cs | 6 +- .../Shaders/DepthStencilClear.metal | 9 +-- 5 files changed, 54 insertions(+), 68 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 3ebf9fbd9..93d84cf57 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Metal public MTLScissorRect[] Scissors = []; // Changes to attachments take recreation! - public MTLTexture DepthStencil = default; + public Texture DepthStencil = default; public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments]; public Dictionary BlendDescriptors = new(); public ColorF BlendColor = new(); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index ae4be9e34..fac656132 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.Metal public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; public readonly PrimitiveTopology Topology => _currentState.Topology; public readonly Texture[] RenderTargets => _currentState.RenderTargets; + public readonly Texture DepthStencil => _currentState.DepthStencil; public EncoderStateManager(MTLDevice device, Pipeline pipeline) { @@ -62,36 +63,36 @@ namespace Ryujinx.Graphics.Metal var depthAttachment = renderPassDescriptor.DepthAttachment; var stencilAttachment = renderPassDescriptor.StencilAttachment; - if (_currentState.DepthStencil != IntPtr.Zero) + if (_currentState.DepthStencil != null) { - switch (_currentState.DepthStencil.PixelFormat) + switch (_currentState.DepthStencil.MTLTexture.PixelFormat) { // Depth Only Attachment case MTLPixelFormat.Depth16Unorm: case MTLPixelFormat.Depth32Float: - depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.Texture = _currentState.DepthStencil.MTLTexture; depthAttachment.LoadAction = MTLLoadAction.Load; break; // Stencil Only Attachment case MTLPixelFormat.Stencil8: - stencilAttachment.Texture = _currentState.DepthStencil; + stencilAttachment.Texture = _currentState.DepthStencil.MTLTexture; stencilAttachment.LoadAction = MTLLoadAction.Load; break; // Combined Attachment case MTLPixelFormat.Depth24UnormStencil8: case MTLPixelFormat.Depth32FloatStencil8: - depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.Texture = _currentState.DepthStencil.MTLTexture; depthAttachment.LoadAction = MTLLoadAction.Load; - var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.PixelFormat); - var stencilView = _currentState.DepthStencil.NewTextureView(unpackedFormat); + var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.MTLTexture.PixelFormat); + var stencilView = _currentState.DepthStencil.MTLTexture.NewTextureView(unpackedFormat); stencilAttachment.Texture = stencilView; stencilAttachment.LoadAction = MTLLoadAction.Load; break; default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.MTLTexture.PixelFormat}!"); break; } } @@ -159,29 +160,29 @@ namespace Ryujinx.Graphics.Metal } } - if (_currentState.DepthStencil != IntPtr.Zero) + if (_currentState.DepthStencil != null) { - switch (_currentState.DepthStencil.PixelFormat) + switch (_currentState.DepthStencil.MTLTexture.PixelFormat) { // Depth Only Attachment case MTLPixelFormat.Depth16Unorm: case MTLPixelFormat.Depth32Float: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.MTLTexture.PixelFormat; break; // Stencil Only Attachment case MTLPixelFormat.Stencil8: - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.MTLTexture.PixelFormat; break; // Combined Attachment case MTLPixelFormat.Depth24UnormStencil8: case MTLPixelFormat.Depth32FloatStencil8: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.MTLTexture.PixelFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.MTLTexture.PixelFormat; break; default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.PixelFormat}!"); + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.MTLTexture.PixelFormat}!"); break; } } @@ -262,7 +263,7 @@ namespace Ryujinx.Graphics.Metal if (depthStencil is Texture depthTexture) { - _currentState.DepthStencil = depthTexture.MTLTexture; + _currentState.DepthStencil = depthTexture; } // Requires recreating pipeline diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 8e95442cd..a1dab8a5b 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -47,33 +47,12 @@ namespace Ryujinx.Graphics.Metal new ShaderSource(colorClearSource, ShaderStage.Vertex, TargetLanguage.Msl) ], device); - // var colorClearFSource = ReadMsl("ColorClearF.metal"); - // _programColorClearF = new Program( - // [ - // new ShaderSource(colorClearFSource, ShaderStage.Fragment, TargetLanguage.Msl), - // new ShaderSource(colorClearFSource, ShaderStage.Vertex, TargetLanguage.Msl) - // ], device); - // - // var colorClearSiSource = ReadMsl("ColorClearSI.metal"); - // _programColorClearSI = new Program( - // [ - // new ShaderSource(colorClearSiSource, ShaderStage.Fragment, TargetLanguage.Msl), - // new ShaderSource(colorClearSiSource, ShaderStage.Vertex, TargetLanguage.Msl) - // ], device); - // - // var colorClearUiSource = ReadMsl("ColorClearUI.metal"); - // _programColorClearUI = new Program( - // [ - // new ShaderSource(colorClearUiSource, ShaderStage.Fragment, TargetLanguage.Msl), - // new ShaderSource(colorClearUiSource, ShaderStage.Vertex, TargetLanguage.Msl) - // ], device); - // - // var depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); - // _programDepthStencilClear = new Program( - // [ - // new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), - // new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) - // ], device); + var depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); + _programDepthStencilClear = new Program( + [ + new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], device); } private static string ReadMsl(string fileName) @@ -129,34 +108,35 @@ namespace Ryujinx.Graphics.Metal _pipeline.Finish(); } - public void ClearDepthStencil( + public unsafe void ClearDepthStencil( Texture dst, - float depthValue, + ReadOnlySpan depthValue, bool depthMask, int stencilValue, - int stencilMask, - int dstWidth, - int dstHeight, - Rectangle scissor) + int stencilMask) { - Span viewports = stackalloc Viewport[1]; + const int ClearColorBufferSize = 16; - viewports[0] = new Viewport( - new Rectangle(0, 0, dstWidth, dstHeight), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); + var buffer = _device.NewBuffer(ClearColorBufferSize, MTLResourceOptions.ResourceStorageModeManaged); + var span = new Span(buffer.Contents.ToPointer(), ClearColorBufferSize); + depthValue.CopyTo(span); + + buffer.DidModifyRange(new NSRange + { + location = 0, + length = ClearColorBufferSize + }); + + var handle = buffer.NativePtr; + var range = new BufferRange(Unsafe.As(ref handle), 0, ClearColorBufferSize); + + _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); _pipeline.SetProgram(_programDepthStencilClear); - // _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight); - _pipeline.SetViewports(viewports); - _pipeline.SetScissors([scissor]); + _pipeline.SetRenderTargets([], dst); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); - _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); + // _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); _pipeline.Draw(4, 1, 0, 0); _pipeline.Finish(); } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index ce9686b08..172439675 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -215,7 +215,11 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + Texture target = _encoderStateManager.DepthStencil; + + _encoderStateManager.SwapStates(); + + _helperShader.ClearDepthStencil(target, [depthValue], depthMask, stencilValue, stencilMask); } public void CommandBufferBarrier() diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal index 0a4e10a25..ad22837fd 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal @@ -7,8 +7,8 @@ struct VertexOut { }; struct FragmentOut { - float4 color [[color(0)]]; float depth [[depth(any)]]; + uint stencil [[stencil]]; }; vertex VertexOut vertexMain(ushort vid [[vertex_id]]) @@ -26,12 +26,13 @@ vertex VertexOut vertexMain(ushort vid [[vertex_id]]) return out; } -fragment float4 fragmentMain(VertexOut in [[stage_in]], - constant float clear_color [[buffer(0)]]) +fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], + constant float& clear_color [[buffer(0)]]) { - Fragment out; + FragmentOut out; out.depth = clear_color; + // out.stencil = stencil_clear; return out; } -- 2.47.1 From 11c596a18abbcd228c07b6a91556844b00b142a7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 21:32:24 -0400 Subject: [PATCH 160/368] =?UTF-8?q?Fix=20StoreActions=20&=20Don=E2=80=99t?= =?UTF-8?q?=20Clamp=20Scissor=20for=20Now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index fac656132..2c6893c92 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -1,7 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; -using SharpMetal.Foundation; using SharpMetal.Metal; using System; using System.Collections.Generic; @@ -57,6 +56,7 @@ namespace Ryujinx.Graphics.Metal var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); passAttachment.Texture = _currentState.RenderTargets[i].MTLTexture; passAttachment.LoadAction = MTLLoadAction.Load; + passAttachment.StoreAction = MTLStoreAction.Store; } } @@ -72,12 +72,14 @@ namespace Ryujinx.Graphics.Metal case MTLPixelFormat.Depth32Float: depthAttachment.Texture = _currentState.DepthStencil.MTLTexture; depthAttachment.LoadAction = MTLLoadAction.Load; + depthAttachment.StoreAction = MTLStoreAction.Store; break; // Stencil Only Attachment case MTLPixelFormat.Stencil8: stencilAttachment.Texture = _currentState.DepthStencil.MTLTexture; stencilAttachment.LoadAction = MTLLoadAction.Load; + stencilAttachment.StoreAction = MTLStoreAction.Store; break; // Combined Attachment @@ -85,11 +87,13 @@ namespace Ryujinx.Graphics.Metal case MTLPixelFormat.Depth32FloatStencil8: depthAttachment.Texture = _currentState.DepthStencil.MTLTexture; depthAttachment.LoadAction = MTLLoadAction.Load; + depthAttachment.StoreAction = MTLStoreAction.Store; var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.MTLTexture.PixelFormat); var stencilView = _currentState.DepthStencil.MTLTexture.NewTextureView(unpackedFormat); stencilAttachment.Texture = stencilView; stencilAttachment.LoadAction = MTLLoadAction.Load; + stencilAttachment.StoreAction = MTLStoreAction.Store; break; default: Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.MTLTexture.PixelFormat}!"); @@ -385,8 +389,8 @@ namespace Ryujinx.Graphics.Metal _currentState.Scissors[i] = new MTLScissorRect { - height = Math.Clamp((ulong)region.Height, 0, (ulong)_currentState.Viewports[i].height), - width = Math.Clamp((ulong)region.Width, 0, (ulong)_currentState.Viewports[i].width), + height = (ulong)region.Height, + width = (ulong)region.Width, x = (ulong)region.X, y = (ulong)region.Y }; -- 2.47.1 From ad4db6b2421a863752c8767184e627b154c8b447 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 22:01:39 -0400 Subject: [PATCH 161/368] Fix typo in SamplerType.TextureBuffer --- src/Ryujinx.Graphics.Shader/SamplerType.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index de43d7aa6..67c508012 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -174,7 +174,7 @@ namespace Ryujinx.Graphics.Shader typeName = (type & SamplerType.Mask) switch { SamplerType.Texture1D => "texture1d", - SamplerType.TextureBuffer => "texturebuffer", + SamplerType.TextureBuffer => "texture_buffer", SamplerType.Texture2D => "texture2d", SamplerType.Texture3D => "texture3d", SamplerType.TextureCube => "texturecube", -- 2.47.1 From c7dc9ba34ee5d814c4ac4b83dd39e01a9c7fff00 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 23 May 2024 15:48:20 +0200 Subject: [PATCH 162/368] implement depth stencil cache --- .../DepthStencilCache.cs | 73 +++++++++++++++++++ .../EncoderStateManager.cs | 6 +- 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/DepthStencilCache.cs diff --git a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs new file mode 100644 index 000000000..8456a9228 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs @@ -0,0 +1,73 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public struct DepthStencilHash + { + public struct StencilHash + { + public MTLStencilOperation StencilFailureOperation; + public MTLStencilOperation DepthFailureOperation; + public MTLStencilOperation DepthStencilPassOperation; + public MTLCompareFunction StencilCompareFunction; + public uint ReadMask; + public uint WriteMask; + } + public StencilHash FrontFace; + public StencilHash BackFace; + public MTLCompareFunction DepthCompareFunction; + public bool DepthWriteEnabled; + } + + [SupportedOSPlatform("macos")] + public class DepthStencilCache : StateCache + { + private readonly MTLDevice _device; + + public DepthStencilCache(MTLDevice device) { + _device = device; + } + + protected override DepthStencilHash GetHash(MTLDepthStencilDescriptor descriptor) { + var hash = new DepthStencilHash(); + + // Front face + hash.FrontFace = new DepthStencilHash.StencilHash { + StencilFailureOperation = descriptor.FrontFaceStencil.StencilFailureOperation, + DepthFailureOperation = descriptor.FrontFaceStencil.DepthFailureOperation, + DepthStencilPassOperation = descriptor.FrontFaceStencil.DepthStencilPassOperation, + StencilCompareFunction = descriptor.FrontFaceStencil.StencilCompareFunction, + ReadMask = descriptor.FrontFaceStencil.ReadMask, + WriteMask = descriptor.FrontFaceStencil.WriteMask + }; + + // Back face + hash.BackFace = new DepthStencilHash.StencilHash { + StencilFailureOperation = descriptor.BackFaceStencil.StencilFailureOperation, + DepthFailureOperation = descriptor.BackFaceStencil.DepthFailureOperation, + DepthStencilPassOperation = descriptor.BackFaceStencil.DepthStencilPassOperation, + StencilCompareFunction = descriptor.BackFaceStencil.StencilCompareFunction, + ReadMask = descriptor.BackFaceStencil.ReadMask, + WriteMask = descriptor.BackFaceStencil.WriteMask + }; + + // Depth + hash.DepthCompareFunction = descriptor.DepthCompareFunction; + hash.DepthWriteEnabled = descriptor.DepthWriteEnabled; + + return hash; + } + + protected override MTLDepthStencilState CreateValue(MTLDepthStencilDescriptor descriptor) + { + return _device.NewDepthStencilState(descriptor); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 2c6893c92..fc8be20c1 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Metal private readonly Pipeline _pipeline; private readonly RenderPipelineCache RenderPipelineCache; + private readonly DepthStencilCache DepthStencilCache; private EncoderState _currentState = new(); private EncoderState _backState = new(); @@ -32,6 +33,7 @@ namespace Ryujinx.Graphics.Metal _device = device; _pipeline = pipeline; RenderPipelineCache = new(device); + DepthStencilCache = new(device); } public void SwapStates() @@ -328,7 +330,7 @@ namespace Ryujinx.Graphics.Metal descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; } - _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); + _currentState.DepthStencilState = DepthStencilCache.GetOrCreate(descriptor); // Mark dirty _currentState.Dirty.DepthStencil = true; @@ -352,7 +354,7 @@ namespace Ryujinx.Graphics.Metal descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; } - _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); + _currentState.DepthStencilState = DepthStencilCache.GetOrCreate(descriptor); // Mark dirty _currentState.Dirty.DepthStencil = true; -- 2.47.1 From a10b0230c38d21ba62369ce54e1420f947074459 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 23 May 2024 17:49:22 +0200 Subject: [PATCH 163/368] do texture barrier --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 172439675..ea64ae3bb 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -514,10 +514,9 @@ namespace Ryujinx.Graphics.Metal public void TextureBarrier() { - // var renderCommandEncoder = GetOrCreateRenderEncoder(); + var renderCommandEncoder = GetOrCreateRenderEncoder(); - // renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, ); - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment); } public void TextureBarrierTiled() -- 2.47.1 From 94e077ca27c92f347f29de98d0cf32d662b33c09 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 23 May 2024 18:23:01 +0200 Subject: [PATCH 164/368] do texture barrier tiled --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index ea64ae3bb..d19a774f8 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -521,10 +521,7 @@ namespace Ryujinx.Graphics.Metal public void TextureBarrierTiled() { - // var renderCommandEncoder = GetOrCreateRenderEncoder(); - - // renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, ); - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + TextureBarrier(); } public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) -- 2.47.1 From 58fb8564a87ebe42ebbfdfec8516bd5d89f1d03c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 23 May 2024 13:15:23 -0400 Subject: [PATCH 165/368] Start Proper Dispose --- Directory.Packages.props | 2 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 20 +++++++++++--------- src/Ryujinx.Graphics.Metal/Program.cs | 4 +++- src/Ryujinx.Graphics.Metal/Sampler.cs | 1 + src/Ryujinx.Graphics.Metal/Texture.cs | 1 + 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index f859d4532..3da0236d3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index b5496028c..6e844e7c0 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -215,17 +215,19 @@ namespace Ryujinx.Graphics.Metal { var blitEncoder = _pipeline.GetOrCreateBlitEncoder(); - MTLBuffer src = _device.NewBuffer((ulong)data.Length, MTLResourceOptions.ResourceStorageModeManaged); - var span = new Span(src.Contents.ToPointer(), data.Length); - data.CopyTo(span); - src.DidModifyRange(new NSRange + using MTLBuffer src = _device.NewBuffer((ulong)data.Length, MTLResourceOptions.ResourceStorageModeManaged); { - location = 0, - length = (ulong)data.Length - }); + var span = new Span(src.Contents.ToPointer(), data.Length); + data.CopyTo(span); + src.DidModifyRange(new NSRange + { + location = 0, + length = (ulong)data.Length + }); - MTLBuffer dst = new(Unsafe.As(ref buffer)); - blitEncoder.CopyFromBuffer(src, 0, dst, (ulong)offset, (ulong)data.Length); + MTLBuffer dst = new(Unsafe.As(ref buffer)); + blitEncoder.CopyFromBuffer(src, 0, dst, (ulong)offset, (ulong)data.Length); + } } public void UpdateCounters() diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 764bcf126..ee0ce4f78 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -63,7 +63,9 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { - return; + VertexFunction.Dispose(); + FragmentFunction.Dispose(); + ComputeFunction.Dispose(); } } } diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index 5cb898a9f..f416b5da5 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -45,6 +45,7 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { + _mtlSamplerState.Dispose(); } } } diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index a50d416aa..524cd6cf9 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -329,6 +329,7 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { MTLTexture.SetPurgeableState(MTLPurgeableState.Volatile); + MTLTexture.Dispose(); } } } -- 2.47.1 From bea46ff9ce747606ed590fa85bb1ae3dc769758b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 23 May 2024 14:08:34 -0400 Subject: [PATCH 166/368] Cleanup + Format --- .../DepthStencilCache.cs | 60 +++++++++---------- src/Ryujinx.Graphics.Metal/EncoderState.cs | 3 +- .../EncoderStateManager.cs | 33 +++++----- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 2 +- src/Ryujinx.Graphics.Metal/HelperShader.cs | 7 --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 1 - .../RenderPipelineCache.cs | 31 +++++----- src/Ryujinx.Graphics.Metal/StateCache.cs | 19 +++--- src/Ryujinx.Graphics.Metal/Window.cs | 2 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 6 -- 10 files changed, 71 insertions(+), 93 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs index 8456a9228..1964d093b 100644 --- a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs +++ b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs @@ -1,9 +1,4 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; using SharpMetal.Metal; -using System; -using System.Collections.Generic; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -31,37 +26,40 @@ namespace Ryujinx.Graphics.Metal { private readonly MTLDevice _device; - public DepthStencilCache(MTLDevice device) { + public DepthStencilCache(MTLDevice device) + { _device = device; } - protected override DepthStencilHash GetHash(MTLDepthStencilDescriptor descriptor) { - var hash = new DepthStencilHash(); - - // Front face - hash.FrontFace = new DepthStencilHash.StencilHash { - StencilFailureOperation = descriptor.FrontFaceStencil.StencilFailureOperation, - DepthFailureOperation = descriptor.FrontFaceStencil.DepthFailureOperation, - DepthStencilPassOperation = descriptor.FrontFaceStencil.DepthStencilPassOperation, - StencilCompareFunction = descriptor.FrontFaceStencil.StencilCompareFunction, - ReadMask = descriptor.FrontFaceStencil.ReadMask, - WriteMask = descriptor.FrontFaceStencil.WriteMask + protected override DepthStencilHash GetHash(MTLDepthStencilDescriptor descriptor) + { + var hash = new DepthStencilHash + { + // Front face + FrontFace = new DepthStencilHash.StencilHash + { + StencilFailureOperation = descriptor.FrontFaceStencil.StencilFailureOperation, + DepthFailureOperation = descriptor.FrontFaceStencil.DepthFailureOperation, + DepthStencilPassOperation = descriptor.FrontFaceStencil.DepthStencilPassOperation, + StencilCompareFunction = descriptor.FrontFaceStencil.StencilCompareFunction, + ReadMask = descriptor.FrontFaceStencil.ReadMask, + WriteMask = descriptor.FrontFaceStencil.WriteMask + }, + // Back face + BackFace = new DepthStencilHash.StencilHash + { + StencilFailureOperation = descriptor.BackFaceStencil.StencilFailureOperation, + DepthFailureOperation = descriptor.BackFaceStencil.DepthFailureOperation, + DepthStencilPassOperation = descriptor.BackFaceStencil.DepthStencilPassOperation, + StencilCompareFunction = descriptor.BackFaceStencil.StencilCompareFunction, + ReadMask = descriptor.BackFaceStencil.ReadMask, + WriteMask = descriptor.BackFaceStencil.WriteMask + }, + // Depth + DepthCompareFunction = descriptor.DepthCompareFunction, + DepthWriteEnabled = descriptor.DepthWriteEnabled }; - // Back face - hash.BackFace = new DepthStencilHash.StencilHash { - StencilFailureOperation = descriptor.BackFaceStencil.StencilFailureOperation, - DepthFailureOperation = descriptor.BackFaceStencil.DepthFailureOperation, - DepthStencilPassOperation = descriptor.BackFaceStencil.DepthStencilPassOperation, - StencilCompareFunction = descriptor.BackFaceStencil.StencilCompareFunction, - ReadMask = descriptor.BackFaceStencil.ReadMask, - WriteMask = descriptor.BackFaceStencil.WriteMask - }; - - // Depth - hash.DepthCompareFunction = descriptor.DepthCompareFunction; - hash.DepthWriteEnabled = descriptor.DepthWriteEnabled; - return hash; } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 93d84cf57..919677732 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -12,7 +12,8 @@ namespace Ryujinx.Graphics.Metal public DirtyFlags() { } - public void MarkAll() { + public void MarkAll() + { Pipeline = true; DepthStencil = true; } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index fc8be20c1..a6a9a149d 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -12,11 +12,10 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] struct EncoderStateManager { - private readonly MTLDevice _device; private readonly Pipeline _pipeline; - private readonly RenderPipelineCache RenderPipelineCache; - private readonly DepthStencilCache DepthStencilCache; + private readonly RenderPipelineCache _renderPipelineCache; + private readonly DepthStencilCache _depthStencilCache; private EncoderState _currentState = new(); private EncoderState _backState = new(); @@ -30,10 +29,9 @@ namespace Ryujinx.Graphics.Metal public EncoderStateManager(MTLDevice device, Pipeline pipeline) { - _device = device; _pipeline = pipeline; - RenderPipelineCache = new(device); - DepthStencilCache = new(device); + _renderPipelineCache = new(device); + _depthStencilCache = new(device); } public void SwapStates() @@ -139,7 +137,8 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty.Clear(); } - private void SetPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { + private readonly void SetPipelineState(MTLRenderCommandEncoder renderCommandEncoder) + { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); for (int i = 0; i < Constants.MaxColorAttachments; i++) @@ -209,7 +208,7 @@ namespace Ryujinx.Graphics.Metal renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; } - var pipelineState = RenderPipelineCache.GetOrCreate(renderPipelineDescriptor); + var pipelineState = _renderPipelineCache.GetOrCreate(renderPipelineDescriptor); renderCommandEncoder.SetRenderPipelineState(pipelineState); @@ -330,7 +329,7 @@ namespace Ryujinx.Graphics.Metal descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; } - _currentState.DepthStencilState = DepthStencilCache.GetOrCreate(descriptor); + _currentState.DepthStencilState = _depthStencilCache.GetOrCreate(descriptor); // Mark dirty _currentState.Dirty.DepthStencil = true; @@ -354,7 +353,7 @@ namespace Ryujinx.Graphics.Metal descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; } - _currentState.DepthStencilState = DepthStencilCache.GetOrCreate(descriptor); + _currentState.DepthStencilState = _depthStencilCache.GetOrCreate(descriptor); // Mark dirty _currentState.Dirty.DepthStencil = true; @@ -556,7 +555,7 @@ namespace Ryujinx.Graphics.Metal } } - private void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) + private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { if (_currentState.DepthStencilState != null) { @@ -564,7 +563,7 @@ namespace Ryujinx.Graphics.Metal } } - private void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder) + private readonly void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder) { renderCommandEncoder.SetDepthClipMode(_currentState.DepthClipMode); } @@ -591,7 +590,7 @@ namespace Ryujinx.Graphics.Metal } } - private MTLVertexDescriptor BuildVertexDescriptor(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) + private readonly MTLVertexDescriptor BuildVertexDescriptor(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) { var vertexDescriptor = new MTLVertexDescriptor(); uint indexMask = 0; @@ -635,7 +634,7 @@ namespace Ryujinx.Graphics.Metal SetBuffers(renderCommandEncoder, buffers); } - private void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) + private readonly void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) { foreach (var buffer in buffers) { @@ -648,17 +647,17 @@ namespace Ryujinx.Graphics.Metal } } - private void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) + private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) { renderCommandEncoder.SetCullMode(_currentState.CullMode); } - private void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder) + private readonly void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder) { renderCommandEncoder.SetFrontFacingWinding(_currentState.Winding); } - private void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary textures, Dictionary samplers) + private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary textures, Dictionary samplers) { foreach (var texture in textures) { diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index dbde36f5e..81a84f668 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Metal Format.R16G16B16A16Float => MTLVertexFormat.Half4, Format.R32Float => MTLVertexFormat.Float, Format.R32G32Float => MTLVertexFormat.Float2, - Format.R32G32B32Float=> MTLVertexFormat.Float3, + Format.R32G32B32Float => MTLVertexFormat.Float3, Format.R11G11B10Float => MTLVertexFormat.FloatRG11B10, Format.R32G32B32A32Float => MTLVertexFormat.Float4, Format.R8Uint => MTLVertexFormat.UChar, diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index a1dab8a5b..2537d6f36 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -10,13 +10,6 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - enum ComponentType - { - Float, - SignedInteger, - UnsignedInteger, - } - [SupportedOSPlatform("macos")] class HelperShader : IDisposable { diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index d19a774f8..5a0785299 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -5,7 +5,6 @@ using SharpMetal.Foundation; using SharpMetal.Metal; using SharpMetal.QuartzCore; using System; -using System.Drawing; using System.Runtime.CompilerServices; using System.Runtime.Versioning; diff --git a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs index 1da672134..d564ef629 100644 --- a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs +++ b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs @@ -1,9 +1,7 @@ using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; using SharpMetal.Foundation; using SharpMetal.Metal; using System; -using System.Collections.Generic; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -72,16 +70,24 @@ namespace Ryujinx.Graphics.Metal { private readonly MTLDevice _device; - public RenderPipelineCache(MTLDevice device) { + public RenderPipelineCache(MTLDevice device) + { _device = device; } - protected override RenderPipelineHash GetHash(MTLRenderPipelineDescriptor descriptor) { - var hash = new RenderPipelineHash(); - - // Functions - hash.VertexFunction = descriptor.VertexFunction; - hash.FragmentFunction = descriptor.FragmentFunction; + protected override RenderPipelineHash GetHash(MTLRenderPipelineDescriptor descriptor) + { + var hash = new RenderPipelineHash + { + // Functions + VertexFunction = descriptor.VertexFunction, + FragmentFunction = descriptor.FragmentFunction, + DepthStencilAttachment = new RenderPipelineHash.DepthStencilAttachmentHash + { + DepthPixelFormat = descriptor.DepthAttachmentPixelFormat, + StencilPixelFormat = descriptor.StencilAttachmentPixelFormat + }, + }; // Color Attachments for (int i = 0; i < Constants.MaxColorAttachments; i++) @@ -100,13 +106,6 @@ namespace Ryujinx.Graphics.Metal }; } - // Depth stencil attachment - hash.DepthStencilAttachment = new RenderPipelineHash.DepthStencilAttachmentHash - { - DepthPixelFormat = descriptor.DepthAttachmentPixelFormat, - StencilPixelFormat = descriptor.StencilAttachmentPixelFormat - }; - // Vertex descriptor hash.VertexDescriptor = new RenderPipelineHash.VertexDescriptorHash(); diff --git a/src/Ryujinx.Graphics.Metal/StateCache.cs b/src/Ryujinx.Graphics.Metal/StateCache.cs index 66fefcf7b..2abf5f528 100644 --- a/src/Ryujinx.Graphics.Metal/StateCache.cs +++ b/src/Ryujinx.Graphics.Metal/StateCache.cs @@ -1,33 +1,28 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; using System.Collections.Generic; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public abstract class StateCache + public abstract class StateCache { - private Dictionary Cache = new(); + private readonly Dictionary _cache = new(); - protected abstract HashT GetHash(DescriptorT descriptor); + protected abstract THash GetHash(TDescriptor descriptor); - protected abstract T CreateValue(DescriptorT descriptor); + protected abstract T CreateValue(TDescriptor descriptor); - public T GetOrCreate(DescriptorT descriptor) + public T GetOrCreate(TDescriptor descriptor) { var hash = GetHash(descriptor); - if (Cache.TryGetValue(hash, out T value)) + if (_cache.TryGetValue(hash, out T value)) { return value; } else { var newValue = CreateValue(descriptor); - Cache.Add(hash, newValue); + _cache.Add(hash, newValue); return newValue; } diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index a656ce26b..64410df6d 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { - + _metalLayer.Dispose(); } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index b771ec12e..24c07e97c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -246,12 +246,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return resourceDefinitions.Textures[textOp.Binding].Name; } - // TODO: Verify that this is valid in MSL - private static string GetMask(int index) - { - return $".{"rgba".AsSpan(index, 1)}"; - } - private static string GetMaskMultiDest(int mask) { string swizzle = "."; -- 2.47.1 From 91aed4d0dd556a61a0624aa1643b52a505db9939 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 23 May 2024 14:47:05 -0400 Subject: [PATCH 167/368] Rebase --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 6e844e7c0..2b535c579 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -79,11 +79,7 @@ namespace Ryujinx.Graphics.Metal public BufferHandle CreateBuffer(int size, BufferAccess access) { var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); - - if (access == BufferAccess.FlushPersistent) - { - buffer.SetPurgeableState(MTLPurgeableState.NonVolatile); - } + buffer.SetPurgeableState(MTLPurgeableState.NonVolatile); var bufferPtr = buffer.NativePtr; return Unsafe.As(ref bufferPtr); @@ -140,6 +136,7 @@ namespace Ryujinx.Graphics.Metal return new Capabilities( api: TargetApi.Metal, vendorName: HardwareInfoTools.GetVendor(), + SystemMemoryType.UnifiedMemory, hasFrontFacingBug: false, hasVectorIndexingBug: true, needsFragmentOutputSpecialization: true, -- 2.47.1 From 084b75a398d96ce982452e8e3540aa34592ef630 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:32:02 +0200 Subject: [PATCH 168/368] resolve merge conflicts --- Directory.Packages.props | 4 +++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 44 ++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3da0236d3..abb78d200 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,11 @@ +<<<<<<< HEAD +======= + +>>>>>>> 3eab14be7 (Set scissors & viewports) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 5a0785299..e782c9540 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -440,7 +440,26 @@ namespace Ryujinx.Graphics.Metal public void SetScissors(ReadOnlySpan> regions) { - _encoderStateManager.UpdateScissors(regions); + // TODO: Test max allowed scissor rects on device + var mtlScissorRects = new MTLScissorRect[regions.Length]; + + for (int i = 0; i < regions.Length; i++) + { + var region = regions[i]; + mtlScissorRects[i] = new MTLScissorRect + { + height = (ulong)region.Height, + width = (ulong)region.Width, + x = (ulong)region.X, + y = (ulong)region.Y + }; + } + + fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissorRects, (ulong)regions.Length); + } } public void SetStencilTest(StencilTestDescriptor stencilTest) @@ -508,7 +527,28 @@ namespace Ryujinx.Graphics.Metal public void SetViewports(ReadOnlySpan viewports) { - _encoderStateManager.UpdateViewports(viewports); + // TODO: Test max allowed viewports on device + var mtlViewports = new MTLViewport[viewports.Length]; + + for (int i = 0; i < viewports.Length; i++) + { + var viewport = viewports[i]; + mtlViewports[i] = new MTLViewport + { + originX = viewport.Region.X, + originY = viewport.Region.Y, + width = viewport.Region.Width, + height = viewport.Region.Height, + znear = viewport.DepthNear, + zfar = viewport.DepthFar + }; + } + + fixed (MTLViewport* pMtlViewports = mtlViewports) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); + } } public void TextureBarrier() -- 2.47.1 From 016df3b05061bc48d2de4ba230faa4abc54f5acf Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 14:16:42 +0200 Subject: [PATCH 169/368] prepare for deferred clears --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 84 ++++++++++++++++--- .../Shaders/DepthStencilClear.metal | 8 +- 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index e782c9540..9b01bb2a5 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -18,6 +18,26 @@ namespace Ryujinx.Graphics.Metal None } + [SupportedOSPlatform("macos")] + struct ColorClear + { + public int Layer; + public int LayerCount; + public uint ComponentMask; + public ColorF Color; + } + + [SupportedOSPlatform("macos")] + struct DepthStencilClear + { + public int Layer; + public int LayerCount; + public float DepthValue; + public bool DepthMask; + public int StencilValue; + public int StencilMask; + } + [SupportedOSPlatform("macos")] class Pipeline : IPipeline, IDisposable { @@ -36,6 +56,10 @@ namespace Ryujinx.Graphics.Metal private EncoderStateManager _encoderStateManager; + // Deferred clears + private ColorClear?[] _colorClears = new ColorClear?[Constants.MaxColorAttachments]; + private DepthStencilClear? _depthStencilClear; + public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { _device = device; @@ -152,6 +176,38 @@ namespace Ryujinx.Graphics.Metal return computeCommandEncoder; } + public void ExecuteDeferredColorClears() + { + for (int i = 0; i < Constants.MaxColorAttachments; i++) + { + if (_colorClears[i] != null) + { + ColorF color = _colorClears[i].Value.Color; + float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; + + Texture target = _encoderStateManager.RenderTargets[i]; + + _encoderStateManager.SwapStates(); + + _helperShader.ClearColor(target, colors); + } + _colorClears[i] = null; + } + } + + public void ExecuteDeferredDepthStencilClear() + { + if (_depthStencilClear != null) + { + Texture target = _encoderStateManager.DepthStencil; + + _encoderStateManager.SwapStates(); + + _helperShader.ClearDepthStencil(target, [_depthStencilClear.Value.DepthValue], _depthStencilClear.Value.DepthMask, _depthStencilClear.Value.StencilValue, _depthStencilClear.Value.StencilMask); + } + _depthStencilClear = null; + } + public void Present(CAMetalDrawable drawable, ITexture texture) { if (texture is not Texture tex) @@ -203,22 +259,30 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; + _colorClears[index] = new ColorClear + { + Layer = layer, + LayerCount = layerCount, + ComponentMask = componentMask, + Color = color + }; - Texture target = _encoderStateManager.RenderTargets[index]; - - _encoderStateManager.SwapStates(); - - _helperShader.ClearColor(target, colors); + ExecuteDeferredColorClears(); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - Texture target = _encoderStateManager.DepthStencil; + _depthStencilClear = new DepthStencilClear + { + Layer = layer, + LayerCount = layerCount, + DepthValue = depthValue, + DepthMask = depthMask, + StencilValue = stencilValue, + StencilMask = stencilMask + }; - _encoderStateManager.SwapStates(); - - _helperShader.ClearDepthStencil(target, [depthValue], depthMask, stencilValue, stencilMask); + ExecuteDeferredDepthStencilClear(); } public void CommandBufferBarrier() diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal index ad22837fd..019bf78d4 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal @@ -11,8 +11,7 @@ struct FragmentOut { uint stencil [[stencil]]; }; -vertex VertexOut vertexMain(ushort vid [[vertex_id]]) -{ +vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { int low = vid & 1; int high = vid >> 1; @@ -27,11 +26,10 @@ vertex VertexOut vertexMain(ushort vid [[vertex_id]]) } fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant float& clear_color [[buffer(0)]]) -{ + constant float& clear_depth [[buffer(0)]]) { FragmentOut out; - out.depth = clear_color; + out.depth = clear_depth; // out.stencil = stencil_clear; return out; -- 2.47.1 From a2c0c113806ce3246c4b441cf556d361362378a6 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 14:41:51 +0200 Subject: [PATCH 170/368] revert deferred clears --- .../EncoderStateManager.cs | 1 + src/Ryujinx.Graphics.Metal/Pipeline.cs | 84 +++---------------- 2 files changed, 11 insertions(+), 74 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index a6a9a149d..1320a04af 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -134,6 +134,7 @@ namespace Ryujinx.Graphics.Metal SetDepthStencilState(renderCommandEncoder); } + // Clear the dirty flags _currentState.Dirty.Clear(); } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 9b01bb2a5..e782c9540 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -18,26 +18,6 @@ namespace Ryujinx.Graphics.Metal None } - [SupportedOSPlatform("macos")] - struct ColorClear - { - public int Layer; - public int LayerCount; - public uint ComponentMask; - public ColorF Color; - } - - [SupportedOSPlatform("macos")] - struct DepthStencilClear - { - public int Layer; - public int LayerCount; - public float DepthValue; - public bool DepthMask; - public int StencilValue; - public int StencilMask; - } - [SupportedOSPlatform("macos")] class Pipeline : IPipeline, IDisposable { @@ -56,10 +36,6 @@ namespace Ryujinx.Graphics.Metal private EncoderStateManager _encoderStateManager; - // Deferred clears - private ColorClear?[] _colorClears = new ColorClear?[Constants.MaxColorAttachments]; - private DepthStencilClear? _depthStencilClear; - public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { _device = device; @@ -176,38 +152,6 @@ namespace Ryujinx.Graphics.Metal return computeCommandEncoder; } - public void ExecuteDeferredColorClears() - { - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - if (_colorClears[i] != null) - { - ColorF color = _colorClears[i].Value.Color; - float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; - - Texture target = _encoderStateManager.RenderTargets[i]; - - _encoderStateManager.SwapStates(); - - _helperShader.ClearColor(target, colors); - } - _colorClears[i] = null; - } - } - - public void ExecuteDeferredDepthStencilClear() - { - if (_depthStencilClear != null) - { - Texture target = _encoderStateManager.DepthStencil; - - _encoderStateManager.SwapStates(); - - _helperShader.ClearDepthStencil(target, [_depthStencilClear.Value.DepthValue], _depthStencilClear.Value.DepthMask, _depthStencilClear.Value.StencilValue, _depthStencilClear.Value.StencilMask); - } - _depthStencilClear = null; - } - public void Present(CAMetalDrawable drawable, ITexture texture) { if (texture is not Texture tex) @@ -259,30 +203,22 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - _colorClears[index] = new ColorClear - { - Layer = layer, - LayerCount = layerCount, - ComponentMask = componentMask, - Color = color - }; + float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; - ExecuteDeferredColorClears(); + Texture target = _encoderStateManager.RenderTargets[index]; + + _encoderStateManager.SwapStates(); + + _helperShader.ClearColor(target, colors); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - _depthStencilClear = new DepthStencilClear - { - Layer = layer, - LayerCount = layerCount, - DepthValue = depthValue, - DepthMask = depthMask, - StencilValue = stencilValue, - StencilMask = stencilMask - }; + Texture target = _encoderStateManager.DepthStencil; - ExecuteDeferredDepthStencilClear(); + _encoderStateManager.SwapStates(); + + _helperShader.ClearDepthStencil(target, [depthValue], depthMask, stencilValue, stencilMask); } public void CommandBufferBarrier() -- 2.47.1 From 305a703d4ab719de5d8ba2c105cfcc6692581534 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:09:06 +0200 Subject: [PATCH 171/368] implement save and restore state system --- .../EncoderStateManager.cs | 19 ++++++++++----- src/Ryujinx.Graphics.Metal/HelperShader.cs | 22 ++++++++++++++---- src/Ryujinx.Graphics.Metal/Pipeline.cs | 23 +++++++++++-------- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 1320a04af..1278b5bc7 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -17,8 +17,8 @@ namespace Ryujinx.Graphics.Metal private readonly RenderPipelineCache _renderPipelineCache; private readonly DepthStencilCache _depthStencilCache; - private EncoderState _currentState = new(); - private EncoderState _backState = new(); + public EncoderState _currentState = new(); + public List _backStates = new(); public readonly MTLBuffer IndexBuffer => _currentState.IndexBuffer; public readonly MTLIndexType IndexType => _currentState.IndexType; @@ -34,13 +34,20 @@ namespace Ryujinx.Graphics.Metal _depthStencilCache = new(device); } - public void SwapStates() + public void SaveState() { - (_currentState, _backState) = (_backState, _currentState); + _backStates.Add(_currentState); + } - if (_pipeline.CurrentEncoderType == EncoderType.Render) + public void RestoreState() + { + if (_backStates.Count > 0) { - _pipeline.EndCurrentPass(); + _currentState = _backStates[_backStates.Count - 1]; + _backStates.RemoveAt(_backStates.Count - 1); + } else + { + Logger.Error?.Print(LogClass.Gpu, "No state to restore"); } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 2537d6f36..d5446f615 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -64,12 +64,17 @@ namespace Ryujinx.Graphics.Metal MipFilter = MTLSamplerMipFilter.NotMipmapped }); + // Save current state + _pipeline.SaveState(); + _pipeline.SetProgram(_programColorBlit); _pipeline.SetRenderTargets([destination], null); _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, source, new Sampler(sampler)); _pipeline.SetPrimitiveTopology(PrimitiveTopology.Triangles); _pipeline.Draw(6, 1, 0, 0); - _pipeline.Finish(); + + // Restore previous state + _pipeline.RestoreState(); } public unsafe void ClearColor( @@ -91,6 +96,9 @@ namespace Ryujinx.Graphics.Metal var handle = buffer.NativePtr; var range = new BufferRange(Unsafe.As(ref handle), 0, ClearColorBufferSize); + // Save current state + _pipeline.SaveState(); + _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); _pipeline.SetProgram(_programColorClear); @@ -98,7 +106,9 @@ namespace Ryujinx.Graphics.Metal // _pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); - _pipeline.Finish(); + + // Restore previous state + _pipeline.RestoreState(); } public unsafe void ClearDepthStencil( @@ -123,15 +133,19 @@ namespace Ryujinx.Graphics.Metal var handle = buffer.NativePtr; var range = new BufferRange(Unsafe.As(ref handle), 0, ClearColorBufferSize); + // Save current state + _pipeline.SaveState(); + _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); _pipeline.SetProgram(_programDepthStencilClear); - _pipeline.SetRenderTargets([], dst); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); // _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); _pipeline.Draw(4, 1, 0, 0); - _pipeline.Finish(); + + // Restore previous state + _pipeline.RestoreState(); } private static StencilTestDescriptor CreateStencilTestDescriptor( diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index e782c9540..e3e392177 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -46,6 +46,16 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager = new EncoderStateManager(_device, this); } + public void SaveState() + { + _encoderStateManager.SaveState(); + } + + public void RestoreState() + { + _encoderStateManager.RestoreState(); + } + public MTLRenderCommandEncoder GetOrCreateRenderEncoder() { MTLRenderCommandEncoder renderCommandEncoder; @@ -161,7 +171,7 @@ namespace Ryujinx.Graphics.Metal EndCurrentPass(); - _encoderStateManager.SwapStates(); + SaveState(); // TODO: Clean this up var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); @@ -169,15 +179,14 @@ namespace Ryujinx.Graphics.Metal _helperShader.BlitColor(tex, dest); + EndCurrentPass(); + _commandBuffer.PresentDrawable(drawable); _commandBuffer.Commit(); _commandBuffer = _commandQueue.CommandBuffer(); - } - public void Finish() - { - _encoderStateManager.SwapStates(); + RestoreState(); } public void Barrier() @@ -207,8 +216,6 @@ namespace Ryujinx.Graphics.Metal Texture target = _encoderStateManager.RenderTargets[index]; - _encoderStateManager.SwapStates(); - _helperShader.ClearColor(target, colors); } @@ -216,8 +223,6 @@ namespace Ryujinx.Graphics.Metal { Texture target = _encoderStateManager.DepthStencil; - _encoderStateManager.SwapStates(); - _helperShader.ClearDepthStencil(target, [depthValue], depthMask, stencilValue, stencilMask); } -- 2.47.1 From 8f91b556af5f16a4c253d85e5da20bc00bf50304 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:28:16 +0200 Subject: [PATCH 172/368] don't interrupt render pass before color clear --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 25 +++++++++++++------ src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- .../Shaders/ColorClear.metal | 14 ++++++----- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index d5446f615..2405e8066 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Foundation; using SharpMetal.Metal; using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -18,7 +19,7 @@ namespace Ryujinx.Graphics.Metal private MTLDevice _device; private readonly IProgram _programColorBlit; - private readonly IProgram _programColorClear; + private readonly List _programsColorClear = new(); private readonly IProgram _programDepthStencilClear; public HelperShader(MTLDevice device, Pipeline pipeline) @@ -34,11 +35,15 @@ namespace Ryujinx.Graphics.Metal ], device); var colorClearSource = ReadMsl("ColorClear.metal"); - _programColorClear = new Program( - [ - new ShaderSource(colorClearSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(colorClearSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], device); + for (int i = 0; i < Constants.MaxColorAttachments; i++) + { + var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()); + _programsColorClear.Add(new Program( + [ + new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], device)); + } var depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); _programDepthStencilClear = new Program( @@ -79,6 +84,7 @@ namespace Ryujinx.Graphics.Metal public unsafe void ClearColor( Texture dst, + int index, ReadOnlySpan clearColor) { const int ClearColorBufferSize = 16; @@ -101,7 +107,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); - _pipeline.SetProgram(_programColorClear); + _pipeline.SetProgram(_programsColorClear[index]); _pipeline.SetRenderTargets([dst], null); // _pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); @@ -175,7 +181,10 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { _programColorBlit.Dispose(); - _programColorClear.Dispose(); + foreach (var programColorClear in _programsColorClear) + { + programColorClear.Dispose(); + } _programDepthStencilClear.Dispose(); _pipeline.Dispose(); } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index e3e392177..3d9e108a1 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Metal Texture target = _encoderStateManager.RenderTargets[index]; - _helperShader.ClearColor(target, colors); + _helperShader.ClearColor(target, index, colors); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal index 85ae873e6..087c48606 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal @@ -6,8 +6,7 @@ struct VertexOut { float4 position [[position]]; }; -vertex VertexOut vertexMain(ushort vid [[vertex_id]]) -{ +vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { int low = vid & 1; int high = vid >> 1; @@ -21,8 +20,11 @@ vertex VertexOut vertexMain(ushort vid [[vertex_id]]) return out; } -fragment float4 fragmentMain(VertexOut in [[stage_in]], - constant float4& clear_color [[buffer(0)]]) -{ - return clear_color; +struct FragmentOut { + float4 color [[color(COLOR_ATTACHMENT_INDEX)]]; +}; + +fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], + constant float4& clear_color [[buffer(0)]]) { + return {clear_color}; } -- 2.47.1 From fd4fe013485643f72787b29ec39ba233db072699 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:52:17 +0200 Subject: [PATCH 173/368] fix: incorrect merge stuff --- Directory.Packages.props | 4 --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 44 ++------------------------ 2 files changed, 2 insertions(+), 46 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index abb78d200..3da0236d3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,11 +44,7 @@ -<<<<<<< HEAD -======= - ->>>>>>> 3eab14be7 (Set scissors & viewports) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 3d9e108a1..2f11de985 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -445,26 +445,7 @@ namespace Ryujinx.Graphics.Metal public void SetScissors(ReadOnlySpan> regions) { - // TODO: Test max allowed scissor rects on device - var mtlScissorRects = new MTLScissorRect[regions.Length]; - - for (int i = 0; i < regions.Length; i++) - { - var region = regions[i]; - mtlScissorRects[i] = new MTLScissorRect - { - height = (ulong)region.Height, - width = (ulong)region.Width, - x = (ulong)region.X, - y = (ulong)region.Y - }; - } - - fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissorRects, (ulong)regions.Length); - } + _encoderStateManager.UpdateScissors(regions); } public void SetStencilTest(StencilTestDescriptor stencilTest) @@ -532,28 +513,7 @@ namespace Ryujinx.Graphics.Metal public void SetViewports(ReadOnlySpan viewports) { - // TODO: Test max allowed viewports on device - var mtlViewports = new MTLViewport[viewports.Length]; - - for (int i = 0; i < viewports.Length; i++) - { - var viewport = viewports[i]; - mtlViewports[i] = new MTLViewport - { - originX = viewport.Region.X, - originY = viewport.Region.Y, - width = viewport.Region.Width, - height = viewport.Region.Height, - znear = viewport.DepthNear, - zfar = viewport.DepthFar - }; - } - - fixed (MTLViewport* pMtlViewports = mtlViewports) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); - } + _encoderStateManager.UpdateViewports(viewports); } public void TextureBarrier() -- 2.47.1 From 14607f44719c2547b7decfef07afbcdb7a7a2aa3 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:59:07 +0200 Subject: [PATCH 174/368] make states private --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 1278b5bc7..da3fab54a 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -17,8 +17,8 @@ namespace Ryujinx.Graphics.Metal private readonly RenderPipelineCache _renderPipelineCache; private readonly DepthStencilCache _depthStencilCache; - public EncoderState _currentState = new(); - public List _backStates = new(); + private EncoderState _currentState = new(); + private List _backStates = new(); public readonly MTLBuffer IndexBuffer => _currentState.IndexBuffer; public readonly MTLIndexType IndexType => _currentState.IndexType; -- 2.47.1 From 280efb2ed606120287c92a214d639b15f385a69f Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 16:40:01 +0200 Subject: [PATCH 175/368] set the inline state after restoring state --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index da3fab54a..313d380f4 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -45,6 +45,17 @@ namespace Ryujinx.Graphics.Metal { _currentState = _backStates[_backStates.Count - 1]; _backStates.RemoveAt(_backStates.Count - 1); + + // Set all the inline state, since it might have changed + var renderCommandEncoder = _pipeline.GetOrCreateRenderEncoder(); + SetDepthClamp(renderCommandEncoder); + SetScissors(renderCommandEncoder); + SetViewports(renderCommandEncoder); + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); + SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + SetCullMode(renderCommandEncoder); + SetFrontFace(renderCommandEncoder); } else { Logger.Error?.Print(LogClass.Gpu, "No state to restore"); -- 2.47.1 From 60084f826e1a0122af2e8b764fae24261888a15d Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 17:59:49 +0200 Subject: [PATCH 176/368] remove useless parameters --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 3 --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 8 ++------ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 2405e8066..d26aa345e 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -83,7 +83,6 @@ namespace Ryujinx.Graphics.Metal } public unsafe void ClearColor( - Texture dst, int index, ReadOnlySpan clearColor) { @@ -108,7 +107,6 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); _pipeline.SetProgram(_programsColorClear[index]); - _pipeline.SetRenderTargets([dst], null); // _pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); @@ -118,7 +116,6 @@ namespace Ryujinx.Graphics.Metal } public unsafe void ClearDepthStencil( - Texture dst, ReadOnlySpan depthValue, bool depthMask, int stencilValue, diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 2f11de985..a53d7cc1e 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -214,16 +214,12 @@ namespace Ryujinx.Graphics.Metal { float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; - Texture target = _encoderStateManager.RenderTargets[index]; - - _helperShader.ClearColor(target, index, colors); + _helperShader.ClearColor(index, colors); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - Texture target = _encoderStateManager.DepthStencil; - - _helperShader.ClearDepthStencil(target, [depthValue], depthMask, stencilValue, stencilMask); + _helperShader.ClearDepthStencil([depthValue], depthMask, stencilValue, stencilMask); } public void CommandBufferBarrier() -- 2.47.1 From 78553f31d98e0cfd963c138bb1c6a430bb1084e4 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 18:37:31 +0200 Subject: [PATCH 177/368] do memory barriers --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 3 +++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index d26aa345e..7ff839418 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -73,6 +73,9 @@ namespace Ryujinx.Graphics.Metal _pipeline.SaveState(); _pipeline.SetProgram(_programColorBlit); + // Viewport and scissor needs to be set before render pass begin so as not to bind the old ones + //_pipeline.SetViewports([]); + //_pipeline.SetScissors([]); _pipeline.SetRenderTargets([destination], null); _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, source, new Sampler(sampler)); _pipeline.SetPrimitiveTopology(PrimitiveTopology.Triangles); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index a53d7cc1e..66984c2bb 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -191,7 +191,22 @@ namespace Ryujinx.Graphics.Metal public void Barrier() { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + + if (_currentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); + + var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; + MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment; + renderCommandEncoder.MemoryBarrier(scope, stages, stages); + } else if (_currentEncoderType == EncoderType.Compute) + { + var computeCommandEncoder = GetOrCreateComputeEncoder(); + + // TODO: Should there be a barrier on render targets? + var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures; + computeCommandEncoder.MemoryBarrier(scope); + } } public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) -- 2.47.1 From 2cb5265c8e917607c0fff077ad92a900bf609848 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 18:41:36 +0200 Subject: [PATCH 178/368] warn about barriers --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 66984c2bb..1424c74bc 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -206,6 +206,9 @@ namespace Ryujinx.Graphics.Metal // TODO: Should there be a barrier on render targets? var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures; computeCommandEncoder.MemoryBarrier(scope); + } else + { + Logger.Warning?.Print(LogClass.Gpu, "Barrier called outside of a render or compute pass"); } } -- 2.47.1 From 1ff81393be74607e98b12bd879d8269a70cda282 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 20:03:55 +0200 Subject: [PATCH 179/368] dispose caches --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 8 +++++++- src/Ryujinx.Graphics.Metal/StateCache.cs | 11 ++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 313d380f4..ab9b362ba 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -10,7 +10,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - struct EncoderStateManager + struct EncoderStateManager : IDisposable { private readonly Pipeline _pipeline; @@ -34,6 +34,12 @@ namespace Ryujinx.Graphics.Metal _depthStencilCache = new(device); } + public void Dispose() + { + _renderPipelineCache.Dispose(); + _depthStencilCache.Dispose(); + } + public void SaveState() { _backStates.Add(_currentState); diff --git a/src/Ryujinx.Graphics.Metal/StateCache.cs b/src/Ryujinx.Graphics.Metal/StateCache.cs index 2abf5f528..4b2c6c5a4 100644 --- a/src/Ryujinx.Graphics.Metal/StateCache.cs +++ b/src/Ryujinx.Graphics.Metal/StateCache.cs @@ -1,10 +1,11 @@ +using System; using System.Collections.Generic; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public abstract class StateCache + public abstract class StateCache : IDisposable where T : IDisposable { private readonly Dictionary _cache = new(); @@ -12,6 +13,14 @@ namespace Ryujinx.Graphics.Metal protected abstract T CreateValue(TDescriptor descriptor); + public void Dispose() + { + foreach (T value in _cache.Values) + { + value.Dispose(); + } + } + public T GetOrCreate(TDescriptor descriptor) { var hash = GetHash(descriptor); -- 2.47.1 From af341f88df5038e4994da007b655002cfc423054 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 08:17:43 +0200 Subject: [PATCH 180/368] dispose all objects in encoder state manager --- .../EncoderStateManager.cs | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index ab9b362ba..688f0077b 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -143,6 +143,9 @@ namespace Ryujinx.Graphics.Metal SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + // Cleanup + renderPassDescriptor.Dispose(); + return renderCommandEncoder; } @@ -217,16 +220,13 @@ namespace Ryujinx.Graphics.Metal } } - renderPipelineDescriptor.VertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); + var vertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); + renderPipelineDescriptor.VertexDescriptor = vertexDescriptor; if (_currentState.VertexFunction != null) { renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; } - else - { - return; - } if (_currentState.FragmentFunction != null) { @@ -242,6 +242,10 @@ namespace Ryujinx.Graphics.Metal _currentState.BlendColor.Green, _currentState.BlendColor.Blue, _currentState.BlendColor.Alpha); + + // Cleanup + renderPipelineDescriptor.Dispose(); + vertexDescriptor.Dispose(); } public void UpdateIndexBuffer(BufferRange buffer, IndexType type) @@ -320,7 +324,7 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateStencilState(StencilTestDescriptor stencilTest) { - _currentState.BackFaceStencil = new MTLStencilDescriptor + var backFace = new MTLStencilDescriptor { StencilFailureOperation = stencilTest.BackSFail.Convert(), DepthFailureOperation = stencilTest.BackDpFail.Convert(), @@ -329,8 +333,9 @@ namespace Ryujinx.Graphics.Metal ReadMask = (uint)stencilTest.BackFuncMask, WriteMask = (uint)stencilTest.BackMask }; + _currentState.BackFaceStencil = backFace; - _currentState.FrontFaceStencil = new MTLStencilDescriptor + var frontFace = new MTLStencilDescriptor { StencilFailureOperation = stencilTest.FrontSFail.Convert(), DepthFailureOperation = stencilTest.FrontDpFail.Convert(), @@ -339,6 +344,7 @@ namespace Ryujinx.Graphics.Metal ReadMask = (uint)stencilTest.FrontFuncMask, WriteMask = (uint)stencilTest.FrontMask }; + _currentState.FrontFaceStencil = frontFace; _currentState.StencilTestEnabled = stencilTest.TestEnable; @@ -358,6 +364,11 @@ namespace Ryujinx.Graphics.Metal // Mark dirty _currentState.Dirty.DepthStencil = true; + + // Cleanup + descriptor.Dispose(); + frontFace.Dispose(); + backFace.Dispose(); } // Inlineable @@ -382,6 +393,9 @@ namespace Ryujinx.Graphics.Metal // Mark dirty _currentState.Dirty.DepthStencil = true; + + // Cleanup + descriptor.Dispose(); } // Inlineable -- 2.47.1 From 1c4e527ac27543329a22fd9c7e01e878939ce630 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 08:27:28 +0200 Subject: [PATCH 181/368] dispose encoder state manager --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 1424c74bc..6f3c96cc9 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -577,6 +577,7 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { EndCurrentPass(); + _encoderStateManager.Dispose(); } } } -- 2.47.1 From d5437f3dbfe8de6bd063b09c10c078d4bb79125e Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 08:39:45 +0200 Subject: [PATCH 182/368] reset viewport before blit --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 5 ----- src/Ryujinx.Graphics.Metal/HelperShader.cs | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 688f0077b..d725fd8de 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -416,11 +416,6 @@ namespace Ryujinx.Graphics.Metal { int maxScissors = Math.Min(regions.Length, _currentState.Viewports.Length); - if (maxScissors == 0) - { - return; - } - _currentState.Scissors = new MTLScissorRect[maxScissors]; for (int i = 0; i < maxScissors; i++) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 7ff839418..99ef00b2c 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -74,8 +74,8 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetProgram(_programColorBlit); // Viewport and scissor needs to be set before render pass begin so as not to bind the old ones - //_pipeline.SetViewports([]); - //_pipeline.SetScissors([]); + _pipeline.SetViewports([]); + _pipeline.SetScissors([]); _pipeline.SetRenderTargets([destination], null); _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, source, new Sampler(sampler)); _pipeline.SetPrimitiveTopology(PrimitiveTopology.Triangles); -- 2.47.1 From eb7ec713ec5d13e440d8f955a3a3f7fb84195a1f Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 09:51:56 +0200 Subject: [PATCH 183/368] reset certain state before doing blit or clear --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 99ef00b2c..35c02a90d 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -73,6 +73,8 @@ namespace Ryujinx.Graphics.Metal _pipeline.SaveState(); _pipeline.SetProgram(_programColorBlit); + _pipeline.SetFaceCulling(false, Face.Front); + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); // Viewport and scissor needs to be set before render pass begin so as not to bind the old ones _pipeline.SetViewports([]); _pipeline.SetScissors([]); @@ -110,6 +112,8 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); _pipeline.SetProgram(_programsColorClear[index]); + _pipeline.SetFaceCulling(false, Face.Front); + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); // _pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); @@ -145,6 +149,8 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); _pipeline.SetProgram(_programDepthStencilClear); + _pipeline.SetFaceCulling(false, Face.Front); + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); // _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); -- 2.47.1 From 0abbbdc277456429e292fdc236050dab622b9667 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 09:56:42 +0200 Subject: [PATCH 184/368] allow null depth stencil render targets --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index d725fd8de..525ab0b2d 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -298,6 +298,9 @@ namespace Ryujinx.Graphics.Metal if (depthStencil is Texture depthTexture) { _currentState.DepthStencil = depthTexture; + } else if (depthStencil == null) + { + _currentState.DepthStencil = null; } // Requires recreating pipeline -- 2.47.1 From 9f01cce95f8a22c89eb0468c0bedcc61390af322 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 13:15:30 +0200 Subject: [PATCH 185/368] fix: don't dispose stencil state before using --- .../EncoderStateManager.cs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 525ab0b2d..4a3993cad 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -36,6 +36,10 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { + // State + _currentState.FrontFaceStencil.Dispose(); + _currentState.BackFaceStencil.Dispose(); + _renderPipelineCache.Dispose(); _depthStencilCache.Dispose(); } @@ -327,18 +331,11 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateStencilState(StencilTestDescriptor stencilTest) { - var backFace = new MTLStencilDescriptor - { - StencilFailureOperation = stencilTest.BackSFail.Convert(), - DepthFailureOperation = stencilTest.BackDpFail.Convert(), - DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), - StencilCompareFunction = stencilTest.BackFunc.Convert(), - ReadMask = (uint)stencilTest.BackFuncMask, - WriteMask = (uint)stencilTest.BackMask - }; - _currentState.BackFaceStencil = backFace; + // Cleanup old state + _currentState.FrontFaceStencil.Dispose(); + _currentState.BackFaceStencil.Dispose(); - var frontFace = new MTLStencilDescriptor + _currentState.FrontFaceStencil = new MTLStencilDescriptor { StencilFailureOperation = stencilTest.FrontSFail.Convert(), DepthFailureOperation = stencilTest.FrontDpFail.Convert(), @@ -347,7 +344,16 @@ namespace Ryujinx.Graphics.Metal ReadMask = (uint)stencilTest.FrontFuncMask, WriteMask = (uint)stencilTest.FrontMask }; - _currentState.FrontFaceStencil = frontFace; + + _currentState.BackFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = stencilTest.BackSFail.Convert(), + DepthFailureOperation = stencilTest.BackDpFail.Convert(), + DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), + StencilCompareFunction = stencilTest.BackFunc.Convert(), + ReadMask = (uint)stencilTest.BackFuncMask, + WriteMask = (uint)stencilTest.BackMask + }; _currentState.StencilTestEnabled = stencilTest.TestEnable; @@ -370,8 +376,6 @@ namespace Ryujinx.Graphics.Metal // Cleanup descriptor.Dispose(); - frontFace.Dispose(); - backFace.Dispose(); } // Inlineable -- 2.47.1 From 6ab989ac545a79a4a6dd808f1cba13cd152c2944 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 11:03:45 +0200 Subject: [PATCH 186/368] implement texture get data --- src/Ryujinx.Graphics.Metal/Texture.cs | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 524cd6cf9..89b965ba8 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -196,7 +196,35 @@ namespace Ryujinx.Graphics.Metal public PinnedSpan GetData(int layer, int level) { - throw new NotImplementedException(); + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + + ulong bytesPerRow = (ulong)Info.GetMipStride(level); + ulong length = bytesPerRow * (ulong)Info.Height; + ulong bytesPerImage = 0; + if (MTLTexture.TextureType == MTLTextureType.Type3D) + { + bytesPerImage = length; + } + + unsafe + { + var mtlBuffer = _device.NewBuffer(length, MTLResourceOptions.ResourceStorageModeShared); + + blitCommandEncoder.CopyFromTexture( + MTLTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin(), + new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height, depth = MTLTexture.Depth }, + mtlBuffer, + 0, + bytesPerRow, + bytesPerImage + ); + + // TODO: Dispose the buffer + return new PinnedSpan(mtlBuffer.Contents.ToPointer(), (int)length); + } } // TODO: Handle array formats -- 2.47.1 From 3be47ae4a9b7d4a694746e9f3c3c3b810e251cf6 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 14:38:39 +0200 Subject: [PATCH 187/368] dispose drawable texture view --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 6f3c96cc9..807069f5f 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -187,6 +187,9 @@ namespace Ryujinx.Graphics.Metal _commandBuffer = _commandQueue.CommandBuffer(); RestoreState(); + + // Cleanup + dest.Dispose(); } public void Barrier() -- 2.47.1 From 58b42a114363775d3c8da9600cb4a4fabc844fde Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 14:48:07 +0200 Subject: [PATCH 188/368] dispose temporary metal buffer --- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 89b965ba8..f16029809 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -223,7 +223,7 @@ namespace Ryujinx.Graphics.Metal ); // TODO: Dispose the buffer - return new PinnedSpan(mtlBuffer.Contents.ToPointer(), (int)length); + return new PinnedSpan(mtlBuffer.Contents.ToPointer(), (int)length, () => mtlBuffer.Dispose()); } } -- 2.47.1 From 60ece6d9a1b791a6f6138492aa45b63b9ba9d609 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 15:23:13 +0200 Subject: [PATCH 189/368] dispose all temporary buffers --- src/Ryujinx.Graphics.Metal/Texture.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index f16029809..63da29966 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -222,7 +222,6 @@ namespace Ryujinx.Graphics.Metal bytesPerImage ); - // TODO: Dispose the buffer return new PinnedSpan(mtlBuffer.Contents.ToPointer(), (int)length, () => mtlBuffer.Dispose()); } } @@ -278,6 +277,9 @@ namespace Ryujinx.Graphics.Metal depth = Math.Max(1, depth >> 1); } } + + // Cleanup + mtlBuffer.Dispose(); } public void SetData(IMemoryOwner data, int layer, int level) @@ -309,6 +311,9 @@ namespace Ryujinx.Graphics.Metal (ulong)level, new MTLOrigin() ); + + // Cleanup + mtlBuffer.Dispose(); } } @@ -341,6 +346,9 @@ namespace Ryujinx.Graphics.Metal (ulong)level, new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } ); + + // Cleanup + mtlBuffer.Dispose(); } } -- 2.47.1 From 327c1576f7d730ae1de7042f927e97ade50cf89c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 25 May 2024 12:12:58 -0400 Subject: [PATCH 190/368] Whitespace formatting --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 6 ++++-- src/Ryujinx.Graphics.Metal/Pipeline.cs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 4a3993cad..6f2725b16 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -66,7 +66,8 @@ namespace Ryujinx.Graphics.Metal SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); SetCullMode(renderCommandEncoder); SetFrontFace(renderCommandEncoder); - } else + } + else { Logger.Error?.Print(LogClass.Gpu, "No state to restore"); } @@ -302,7 +303,8 @@ namespace Ryujinx.Graphics.Metal if (depthStencil is Texture depthTexture) { _currentState.DepthStencil = depthTexture; - } else if (depthStencil == null) + } + else if (depthStencil == null) { _currentState.DepthStencil = null; } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 807069f5f..b3be4124d 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -202,14 +202,16 @@ namespace Ryujinx.Graphics.Metal var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment; renderCommandEncoder.MemoryBarrier(scope, stages, stages); - } else if (_currentEncoderType == EncoderType.Compute) + } + else if (_currentEncoderType == EncoderType.Compute) { var computeCommandEncoder = GetOrCreateComputeEncoder(); // TODO: Should there be a barrier on render targets? var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures; computeCommandEncoder.MemoryBarrier(scope); - } else + } + else { Logger.Warning?.Print(LogClass.Gpu, "Barrier called outside of a render or compute pass"); } -- 2.47.1 From 881ab59177448012c4c287b96142cb7d1dfa28c5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 25 May 2024 12:21:42 -0400 Subject: [PATCH 191/368] Use Stack instead of List --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 6f2725b16..a6c931bc1 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Metal private readonly DepthStencilCache _depthStencilCache; private EncoderState _currentState = new(); - private List _backStates = new(); + private readonly Stack _backStates = []; public readonly MTLBuffer IndexBuffer => _currentState.IndexBuffer; public readonly MTLIndexType IndexType => _currentState.IndexType; @@ -44,17 +44,16 @@ namespace Ryujinx.Graphics.Metal _depthStencilCache.Dispose(); } - public void SaveState() + public readonly void SaveState() { - _backStates.Add(_currentState); + _backStates.Push(_currentState); } public void RestoreState() { if (_backStates.Count > 0) { - _currentState = _backStates[_backStates.Count - 1]; - _backStates.RemoveAt(_backStates.Count - 1); + _currentState = _backStates.Pop(); // Set all the inline state, since it might have changed var renderCommandEncoder = _pipeline.GetOrCreateRenderEncoder(); -- 2.47.1 From 41e6a04a231b4d92b7266e3a8bae051ded927ee4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 25 May 2024 12:30:06 -0400 Subject: [PATCH 192/368] Suppress GC Finalize on StateCache --- src/Ryujinx.Graphics.Metal/StateCache.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/StateCache.cs b/src/Ryujinx.Graphics.Metal/StateCache.cs index 4b2c6c5a4..f333814e6 100644 --- a/src/Ryujinx.Graphics.Metal/StateCache.cs +++ b/src/Ryujinx.Graphics.Metal/StateCache.cs @@ -19,6 +19,8 @@ namespace Ryujinx.Graphics.Metal { value.Dispose(); } + + GC.SuppressFinalize(this); } public T GetOrCreate(TDescriptor descriptor) -- 2.47.1 From 18b852e05d83e7fa7f0250d4c3606ef77c147138 Mon Sep 17 00:00:00 2001 From: SamoZ256 <96914946+SamoZ256@users.noreply.github.com> Date: Sat, 25 May 2024 19:46:51 +0200 Subject: [PATCH 193/368] Fix Scott Pilgrim (#15) * check for null vertex functions * format * Format --------- Co-authored-by: Isaac Marovitz --- .../EncoderStateManager.cs | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index a6c931bc1..a15b23a69 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -227,29 +227,38 @@ namespace Ryujinx.Graphics.Metal var vertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); renderPipelineDescriptor.VertexDescriptor = vertexDescriptor; - if (_currentState.VertexFunction != null) + try { - renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; - } + if (_currentState.VertexFunction != null) + { + renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; + } + else + { + return; + } - if (_currentState.FragmentFunction != null) + if (_currentState.FragmentFunction != null) + { + renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; + } + + var pipelineState = _renderPipelineCache.GetOrCreate(renderPipelineDescriptor); + + renderCommandEncoder.SetRenderPipelineState(pipelineState); + + renderCommandEncoder.SetBlendColor( + _currentState.BlendColor.Red, + _currentState.BlendColor.Green, + _currentState.BlendColor.Blue, + _currentState.BlendColor.Alpha); + } + finally { - renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; + // Cleanup + renderPipelineDescriptor.Dispose(); + vertexDescriptor.Dispose(); } - - var pipelineState = _renderPipelineCache.GetOrCreate(renderPipelineDescriptor); - - renderCommandEncoder.SetRenderPipelineState(pipelineState); - - renderCommandEncoder.SetBlendColor( - _currentState.BlendColor.Red, - _currentState.BlendColor.Green, - _currentState.BlendColor.Blue, - _currentState.BlendColor.Alpha); - - // Cleanup - renderPipelineDescriptor.Dispose(); - vertexDescriptor.Dispose(); } public void UpdateIndexBuffer(BufferRange buffer, IndexType type) -- 2.47.1 From 6cc4d46e8c70d56fc7416c79ee885dcf4f69dccb Mon Sep 17 00:00:00 2001 From: SamoZ256 <96914946+SamoZ256@users.noreply.github.com> Date: Mon, 27 May 2024 13:58:03 +0200 Subject: [PATCH 194/368] Clone the state & flip viewport vertically (#16) * implement texture get data * reset all state before blit & clone state * format * support blit regions * implement source region for blit * replace bottom with top * account for 0 size * support image flipping * revert presentation fixes & y flip * revert * flip viewport vertically * switch face winding * comment * use SetBytes for texture clear * implement missing compute builtins * change storage and texture buffer alignment * correct compute builtins * don't use nullable for textures and samplers * remove incorrect texture get data implementation * Cleanup IntPtrs --------- Co-authored-by: Isaac Marovitz --- src/Ryujinx.Graphics.Metal/Constants.cs | 2 + src/Ryujinx.Graphics.Metal/EncoderState.cs | 27 ++++++-- .../EncoderStateManager.cs | 62 ++++++++++++------- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 5 +- src/Ryujinx.Graphics.Metal/HelperShader.cs | 48 ++++---------- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 4 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 7 ++- .../CodeGen/Msl/Instructions/IoMap.cs | 5 +- 8 files changed, 91 insertions(+), 69 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index f20598f9c..c9af5deaf 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -14,5 +14,7 @@ namespace Ryujinx.Graphics.Metal public const int MaxVertexAttributes = 16; // TODO: Check this value public const int MaxVertexLayouts = 16; + public const int MaxTextures = 31; + public const int MaxSamplers = 31; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 919677732..0bd2e9651 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -31,11 +31,11 @@ namespace Ryujinx.Graphics.Metal public MTLFunction? VertexFunction = null; public MTLFunction? FragmentFunction = null; - public Dictionary FragmentTextures = new(); - public Dictionary FragmentSamplers = new(); + public MTLTexture[] FragmentTextures = new MTLTexture[Constants.MaxTextures]; + public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxSamplers]; - public Dictionary VertexTextures = new(); - public Dictionary VertexSamplers = new(); + public MTLTexture[] VertexTextures = new MTLTexture[Constants.MaxTextures]; + public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxSamplers]; public List UniformBuffers = []; public List StorageBuffers = []; @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Metal public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; - public MTLWinding Winding = MTLWinding.Clockwise; + public MTLWinding Winding = MTLWinding.CounterClockwise; public MTLViewport[] Viewports = []; public MTLScissorRect[] Scissors = []; @@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Metal // Changes to attachments take recreation! public Texture DepthStencil = default; public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments]; - public Dictionary BlendDescriptors = new(); + public BlendDescriptor?[] BlendDescriptors = new BlendDescriptor?[Constants.MaxColorAttachments]; public ColorF BlendColor = new(); public VertexBufferDescriptor[] VertexBuffers = []; @@ -74,5 +74,20 @@ namespace Ryujinx.Graphics.Metal public DirtyFlags Dirty = new(); public EncoderState() { } + + public EncoderState Clone() + { + // Certain state (like viewport and scissor) doesn't need to be cloned, as it is always reacreated when assigned to + EncoderState clone = this; + clone.FragmentTextures = (MTLTexture[])FragmentTextures.Clone(); + clone.FragmentSamplers = (MTLSamplerState[])FragmentSamplers.Clone(); + clone.VertexTextures = (MTLTexture[])VertexTextures.Clone(); + clone.VertexSamplers = (MTLSamplerState[])VertexSamplers.Clone(); + clone.BlendDescriptors = (BlendDescriptor?[])BlendDescriptors.Clone(); + clone.VertexBuffers = (VertexBufferDescriptor[])VertexBuffers.Clone(); + clone.VertexAttribs = (VertexAttribDescriptor[])VertexAttribs.Clone(); + + return clone; + } } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index a15b23a69..849461802 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -44,9 +44,16 @@ namespace Ryujinx.Graphics.Metal _depthStencilCache.Dispose(); } - public readonly void SaveState() + public void SaveState() { _backStates.Push(_currentState); + _currentState = _currentState.Clone(); + } + + public void SaveAndResetState() + { + _backStates.Push(_currentState); + _currentState = new(); } public void RestoreState() @@ -65,6 +72,9 @@ namespace Ryujinx.Graphics.Metal SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); SetCullMode(renderCommandEncoder); SetFrontFace(renderCommandEncoder); + + // Mark the other state as dirty + _currentState.Dirty.MarkAll(); } else { @@ -184,8 +194,9 @@ namespace Ryujinx.Graphics.Metal pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - if (_currentState.BlendDescriptors.TryGetValue(i, out BlendDescriptor blendDescriptor)) + if (_currentState.BlendDescriptors[i] != null) { + var blendDescriptor = _currentState.BlendDescriptors[i].Value; pipelineAttachment.SetBlendingEnabled(blendDescriptor.Enable); pipelineAttachment.AlphaBlendOperation = blendDescriptor.AlphaOp.Convert(); pipelineAttachment.RgbBlendOperation = blendDescriptor.ColorOp.Convert(); @@ -469,12 +480,13 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < viewports.Length; i++) { var viewport = viewports[i]; + // Y coordinate is inverted _currentState.Viewports[i] = new MTLViewport { originX = viewport.Region.X, - originY = viewport.Region.Y, + originY = viewport.Region.Y + viewport.Region.Height, width = viewport.Region.Width, - height = viewport.Region.Height, + height = -viewport.Region.Height, znear = Clamp(viewport.DepthNear), zfar = Clamp(viewport.DepthFar) }; @@ -708,31 +720,39 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetFrontFacingWinding(_currentState.Winding); } - private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary textures, Dictionary samplers) + private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, MTLTexture[] textures, MTLSamplerState[] samplers) { - foreach (var texture in textures) + for (int i = 0; i < textures.Length; i++) { - switch (stage) + var texture = textures[i]; + if (texture != IntPtr.Zero) { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexTexture(texture.Value, texture.Key); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentTexture(texture.Value, texture.Key); - break; + switch (stage) + { + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexTexture(texture, (ulong)i); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentTexture(texture, (ulong)i); + break; + } } } - foreach (var sampler in samplers) + for (int i = 0; i < samplers.Length; i++) { - switch (stage) + var sampler = samplers[i]; + if (sampler != IntPtr.Zero) { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexSamplerState(sampler.Value, sampler.Key); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentSamplerState(sampler.Value, sampler.Key); - break; + switch (stage) + { + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexSamplerState(sampler, (ulong)i); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentSamplerState(sampler, (ulong)i); + break; + } } } } diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index 81a84f668..18662e21a 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -93,10 +93,11 @@ namespace Ryujinx.Graphics.Metal public static MTLWinding Convert(this FrontFace frontFace) { + // The viewport is flipped vertically, therefore we need to switch the winding order as well return frontFace switch { - FrontFace.Clockwise => MTLWinding.Clockwise, - FrontFace.CounterClockwise => MTLWinding.CounterClockwise, + FrontFace.Clockwise => MTLWinding.CounterClockwise, + FrontFace.CounterClockwise => MTLWinding.Clockwise, _ => LogInvalidAndReturn(frontFace, nameof(FrontFace), MTLWinding.Clockwise) }; } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 35c02a90d..c2f7a012f 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -2,11 +2,9 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; -using SharpMetal.Foundation; using SharpMetal.Metal; using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -70,11 +68,9 @@ namespace Ryujinx.Graphics.Metal }); // Save current state - _pipeline.SaveState(); + _pipeline.SaveAndResetState(); _pipeline.SetProgram(_programColorBlit); - _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); // Viewport and scissor needs to be set before render pass begin so as not to bind the old ones _pipeline.SetViewports([]); _pipeline.SetScissors([]); @@ -93,29 +89,21 @@ namespace Ryujinx.Graphics.Metal { const int ClearColorBufferSize = 16; - var buffer = _device.NewBuffer(ClearColorBufferSize, MTLResourceOptions.ResourceStorageModeManaged); - var span = new Span(buffer.Contents.ToPointer(), ClearColorBufferSize); - clearColor.CopyTo(span); - - buffer.DidModifyRange(new NSRange - { - location = 0, - length = ClearColorBufferSize - }); - - var handle = buffer.NativePtr; - var range = new BufferRange(Unsafe.As(ref handle), 0, ClearColorBufferSize); - // Save current state _pipeline.SaveState(); - _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); - _pipeline.SetProgram(_programsColorClear[index]); + _pipeline.SetBlendState(index, new BlendDescriptor(false, new ColorF(0f, 0f, 0f, 1f), BlendOp.Add, BlendFactor.One, BlendFactor.Zero, BlendOp.Add, BlendFactor.One, BlendFactor.Zero)); _pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); // _pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + + fixed (float* ptr = clearColor) + { + _pipeline.GetOrCreateRenderEncoder().SetFragmentBytes((IntPtr)ptr, ClearColorBufferSize, 0); + } + _pipeline.Draw(4, 1, 0, 0); // Restore previous state @@ -123,37 +111,25 @@ namespace Ryujinx.Graphics.Metal } public unsafe void ClearDepthStencil( - ReadOnlySpan depthValue, + float depthValue, bool depthMask, int stencilValue, int stencilMask) { - const int ClearColorBufferSize = 16; + const int ClearDepthBufferSize = 4; - var buffer = _device.NewBuffer(ClearColorBufferSize, MTLResourceOptions.ResourceStorageModeManaged); - var span = new Span(buffer.Contents.ToPointer(), ClearColorBufferSize); - depthValue.CopyTo(span); - - buffer.DidModifyRange(new NSRange - { - location = 0, - length = ClearColorBufferSize - }); - - var handle = buffer.NativePtr; - var range = new BufferRange(Unsafe.As(ref handle), 0, ClearColorBufferSize); + IntPtr ptr = new(&depthValue); // Save current state _pipeline.SaveState(); - _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]); - _pipeline.SetProgram(_programDepthStencilClear); _pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); // _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); + _pipeline.GetOrCreateRenderEncoder().SetFragmentBytes(ptr, ClearDepthBufferSize, 0); _pipeline.Draw(4, 1, 0, 0); // Restore previous state diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 2b535c579..55d25c8ae 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -186,8 +186,8 @@ namespace Ryujinx.Graphics.Metal maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength, maximumSupportedAnisotropy: 0, shaderSubgroupSize: 256, - storageBufferOffsetAlignment: 0, - textureBufferOffsetAlignment: 0, + storageBufferOffsetAlignment: 16, + textureBufferOffsetAlignment: 16, gatherBiasPrecision: 0 ); } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index b3be4124d..2734e6930 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -51,6 +51,11 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager.SaveState(); } + public void SaveAndResetState() + { + _encoderStateManager.SaveAndResetState(); + } + public void RestoreState() { _encoderStateManager.RestoreState(); @@ -242,7 +247,7 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - _helperShader.ClearDepthStencil([depthValue], depthMask, stencilValue, stencilMask); + _helperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask); } public void CommandBufferBarrier() diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index d0c198904..b98db242d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -19,18 +19,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { IoVariable.BaseInstance => ("base_instance", AggregateType.S32), IoVariable.BaseVertex => ("base_vertex", AggregateType.S32), + IoVariable.CtaId => ("threadgroup_position_in_grid", AggregateType.Vector3 | AggregateType.U32), IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), IoVariable.FragmentOutputColor => ($"out.color{location}", AggregateType.Vector4 | AggregateType.FP32), IoVariable.FragmentOutputDepth => ("out.depth", AggregateType.FP32), IoVariable.FrontFacing => ("front_facing", AggregateType.Bool), + IoVariable.GlobalId => ("thread_position_in_grid", AggregateType.Vector3 | AggregateType.U32), IoVariable.InstanceId => ("instance_id", AggregateType.S32), + IoVariable.InvocationId => ("INVOCATION_ID", AggregateType.S32), IoVariable.PointCoord => ("point_coord", AggregateType.Vector2), IoVariable.PointSize => ("out.point_size", AggregateType.FP32), IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), + IoVariable.ThreadId => ("thread_position_in_threadgroup", AggregateType.Vector3 | AggregateType.U32), IoVariable.VertexId => ("vertex_id", AggregateType.S32), - IoVariable.GlobalId => ("global_id", AggregateType.Vector3 | AggregateType.U32), // gl_VertexIndex does not have a direct equivalent in MSL IoVariable.VertexIndex => ("vertex_index", AggregateType.U32), IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), -- 2.47.1 From f7941a0a8b732a4db057539251abbf9700ab5537 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 27 May 2024 09:47:50 -0400 Subject: [PATCH 195/368] Metal: Advanced Present (#6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial DrawTexture support & Advanced Present * TODO: Get Scissors Working * Chnage scissor state management * Rebase problems… * Rebase fixes again * Update DrawTexture + Fix Topology * Fix flipping * Add clear action support * Cleanup --- .../Effects/IPostProcessingEffect.cs | 10 ++ .../Effects/IScalingFilter.cs | 18 ++ src/Ryujinx.Graphics.Metal/EncoderState.cs | 3 + .../EncoderStateManager.cs | 26 ++- src/Ryujinx.Graphics.Metal/HelperShader.cs | 149 ++++++++++++++-- src/Ryujinx.Graphics.Metal/Pipeline.cs | 22 ++- src/Ryujinx.Graphics.Metal/Shaders/Blit.metal | 29 ++-- src/Ryujinx.Graphics.Metal/Window.cs | 164 +++++++++++++++++- 8 files changed, 358 insertions(+), 63 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs create mode 100644 src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs diff --git a/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs b/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs new file mode 100644 index 000000000..d575d521f --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs @@ -0,0 +1,10 @@ +using System; + +namespace Ryujinx.Graphics.Metal.Effects +{ + internal interface IPostProcessingEffect : IDisposable + { + const int LocalGroupSize = 64; + Texture Run(Texture view, int width, int height); + } +} diff --git a/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs b/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs new file mode 100644 index 000000000..19f1a3c3d --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs @@ -0,0 +1,18 @@ +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.Metal.Effects +{ + internal interface IScalingFilter : IDisposable + { + float Level { get; set; } + void Run( + Texture view, + Texture destinationTexture, + Format format, + int width, + int height, + Extents2D source, + Extents2D destination); + } +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 0bd2e9651..54f30e985 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -73,6 +73,9 @@ namespace Ryujinx.Graphics.Metal // Dirty flags public DirtyFlags Dirty = new(); + // Only to be used for present + public bool ClearLoadAction = false; + public EncoderState() { } public EncoderState Clone() diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 849461802..d3d23c12c 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -24,8 +24,6 @@ namespace Ryujinx.Graphics.Metal public readonly MTLIndexType IndexType => _currentState.IndexType; public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; public readonly PrimitiveTopology Topology => _currentState.Topology; - public readonly Texture[] RenderTargets => _currentState.RenderTargets; - public readonly Texture DepthStencil => _currentState.DepthStencil; public EncoderStateManager(MTLDevice device, Pipeline pipeline) { @@ -82,6 +80,11 @@ namespace Ryujinx.Graphics.Metal } } + public void SetClearLoadAction(bool clear) + { + _currentState.ClearLoadAction = clear; + } + public MTLRenderCommandEncoder CreateRenderCommandEncoder() { // Initialise Pass & State @@ -93,7 +96,7 @@ namespace Ryujinx.Graphics.Metal { var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); passAttachment.Texture = _currentState.RenderTargets[i].MTLTexture; - passAttachment.LoadAction = MTLLoadAction.Load; + passAttachment.LoadAction = _currentState.ClearLoadAction ? MTLLoadAction.Clear : MTLLoadAction.Load; passAttachment.StoreAction = MTLStoreAction.Store; } } @@ -661,11 +664,18 @@ namespace Ryujinx.Graphics.Metal // TODO: Handle 'zero' buffers for (int i = 0; i < attribDescriptors.Length; i++) { - var attrib = vertexDescriptor.Attributes.Object((ulong)i); - attrib.Format = attribDescriptors[i].Format.Convert(); - indexMask |= 1u << attribDescriptors[i].BufferIndex; - attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; - attrib.Offset = (ulong)attribDescriptors[i].Offset; + if (!attribDescriptors[i].IsZero) + { + var attrib = vertexDescriptor.Attributes.Object((ulong)i); + attrib.Format = attribDescriptors[i].Format.Convert(); + indexMask |= 1u << attribDescriptors[i].BufferIndex; + attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; + attrib.Offset = (ulong)attribDescriptors[i].Offset; + } + else + { + // Logger.Warning?.PrintMsg(LogClass.Gpu, "Unhandled IsZero buffer!"); + } } for (int i = 0; i < bufferDescriptors.Length; i++) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index c2f7a012f..1074a82ee 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Metal private readonly Pipeline _pipeline; private MTLDevice _device; + private readonly ISampler _samplerLinear; + private readonly ISampler _samplerNearest; private readonly IProgram _programColorBlit; private readonly List _programsColorClear = new(); private readonly IProgram _programDepthStencilClear; @@ -25,6 +27,9 @@ namespace Ryujinx.Graphics.Metal _device = device; _pipeline = pipeline; + _samplerNearest = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); + _samplerLinear = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + var blitSource = ReadMsl("Blit.metal"); _programColorBlit = new Program( [ @@ -56,28 +61,140 @@ namespace Ryujinx.Graphics.Metal return EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName)); } - public void BlitColor( - ITexture source, - ITexture destination) + public unsafe void BlitColor( + ITexture src, + ITexture dst, + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter) { - var sampler = _device.NewSamplerState(new MTLSamplerDescriptor + const int RegionBufferSize = 16; + + var sampler = linearFilter ? _samplerLinear : _samplerNearest; + + Span region = stackalloc float[RegionBufferSize / sizeof(float)]; + + region[0] = srcRegion.X1 / src.Width; + region[1] = srcRegion.X2 / src.Width; + region[2] = srcRegion.Y1 / src.Height; + region[3] = srcRegion.Y2 / src.Height; + + if (dstRegion.X1 > dstRegion.X2) { - MinFilter = MTLSamplerMinMagFilter.Nearest, - MagFilter = MTLSamplerMinMagFilter.Nearest, - MipFilter = MTLSamplerMipFilter.NotMipmapped - }); + (region[0], region[1]) = (region[1], region[0]); + } + + if (dstRegion.Y1 > dstRegion.Y2) + { + (region[2], region[3]) = (region[3], region[2]); + } + + var rect = new Rectangle( + MathF.Min(dstRegion.X1, dstRegion.X2), + MathF.Min(dstRegion.Y1, dstRegion.Y2), + MathF.Abs(dstRegion.X2 - dstRegion.X1), + MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); + + Span viewports = stackalloc Viewport[1]; + + viewports[0] = new Viewport( + rect, + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + int dstWidth = dst.Width; + int dstHeight = dst.Height; // Save current state _pipeline.SaveAndResetState(); _pipeline.SetProgram(_programColorBlit); - // Viewport and scissor needs to be set before render pass begin so as not to bind the old ones - _pipeline.SetViewports([]); - _pipeline.SetScissors([]); - _pipeline.SetRenderTargets([destination], null); - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, source, new Sampler(sampler)); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.Triangles); - _pipeline.Draw(6, 1, 0, 0); + _pipeline.SetViewports(viewports); + _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dstWidth, dstHeight) }); + _pipeline.SetRenderTargets([dst], null); + _pipeline.SetClearLoadAction(true); + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + + fixed (float* ptr = region) + { + _pipeline.GetOrCreateRenderEncoder().SetVertexBytes((IntPtr)ptr, RegionBufferSize, 0); + } + + _pipeline.Draw(4, 1, 0, 0); + + // Restore previous state + _pipeline.RestoreState(); + } + + public unsafe void DrawTexture( + ITexture src, + ISampler srcSampler, + Extents2DF srcRegion, + Extents2DF dstRegion) + { + const int RegionBufferSize = 16; + + Span region = stackalloc float[RegionBufferSize / sizeof(float)]; + + region[0] = srcRegion.X1 / src.Width; + region[1] = srcRegion.X2 / src.Width; + region[2] = srcRegion.Y1 / src.Height; + region[3] = srcRegion.Y2 / src.Height; + + if (dstRegion.X1 > dstRegion.X2) + { + (region[0], region[1]) = (region[1], region[0]); + } + + if (dstRegion.Y1 > dstRegion.Y2) + { + (region[2], region[3]) = (region[3], region[2]); + } + + Span viewports = stackalloc Viewport[1]; + Span> scissors = stackalloc Rectangle[1]; + + var rect = new Rectangle( + MathF.Min(dstRegion.X1, dstRegion.X2), + MathF.Min(dstRegion.Y1, dstRegion.Y2), + MathF.Abs(dstRegion.X2 - dstRegion.X1), + MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); + + viewports[0] = new Viewport( + rect, + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + scissors[0] = new Rectangle(0, 0, 0xFFFF, 0xFFFF); + + // Save current state + _pipeline.SaveState(); + + _pipeline.SetProgram(_programColorBlit); + _pipeline.SetViewports(viewports); + _pipeline.SetScissors(scissors); + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + _pipeline.SetFaceCulling(false, Face.FrontAndBack); + // For some reason this results in a SIGSEGV + // _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); + + fixed (float* ptr = region) + { + _pipeline.GetOrCreateRenderEncoder().SetVertexBytes((IntPtr)ptr, RegionBufferSize, 0); + } + + _pipeline.Draw(4, 1, 0, 0); // Restore previous state _pipeline.RestoreState(); @@ -169,6 +286,8 @@ namespace Ryujinx.Graphics.Metal } _programDepthStencilClear.Dispose(); _pipeline.Dispose(); + _samplerLinear.Dispose(); + _samplerNearest.Dispose(); } } } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 2734e6930..2d66a36f0 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -61,6 +61,11 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager.RestoreState(); } + public void SetClearLoadAction(bool clear) + { + _encoderStateManager.SetClearLoadAction(clear); + } + public MTLRenderCommandEncoder GetOrCreateRenderEncoder() { MTLRenderCommandEncoder renderCommandEncoder; @@ -167,22 +172,17 @@ namespace Ryujinx.Graphics.Metal return computeCommandEncoder; } - public void Present(CAMetalDrawable drawable, ITexture texture) + public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear) { - if (texture is not Texture tex) - { - return; - } - EndCurrentPass(); SaveState(); // TODO: Clean this up var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); - var dest = new Texture(_device, this, textureInfo, drawable.Texture, 0, 0); + var dst = new Texture(_device, this, textureInfo, drawable.Texture, 0, 0); - _helperShader.BlitColor(tex, dest); + _helperShader.BlitColor(src, dst, srcRegion, dstRegion, isLinear); EndCurrentPass(); @@ -194,7 +194,7 @@ namespace Ryujinx.Graphics.Metal RestoreState(); // Cleanup - dest.Dispose(); + dst.Dispose(); } public void Barrier() @@ -338,9 +338,7 @@ namespace Ryujinx.Graphics.Metal public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(); - - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _helperShader.DrawTexture(texture, sampler, srcRegion, dstRegion); } public void SetAlphaTest(bool enable, float reference, CompareOp op) diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index b2bec3e8e..3d86a27a8 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -2,32 +2,23 @@ using namespace metal; -// ------------------ -// Simple Blit Shader -// ------------------ - -constant float2 quadVertices[] = { - float2(-1, -1), - float2(-1, 1), - float2( 1, 1), - float2(-1, -1), - float2( 1, 1), - float2( 1, -1) -}; - struct CopyVertexOut { float4 position [[position]]; float2 uv; }; -vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) { - float2 position = quadVertices[vid]; - +vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], + const device float* texCoord [[buffer(0)]]) { CopyVertexOut out; - out.position = float4(position, 0, 1); - out.position.y = -out.position.y; - out.uv = position * 0.5f + 0.5f; + int low = vid & 1; + int high = vid >> 1; + out.uv.x = texCoord[low]; + out.uv.y = texCoord[2 + high]; + out.position.x = (float(low) - 0.5f) * 2.0f; + out.position.y = (float(high) - 0.5f) * 2.0f; + out.position.z = 0.0f; + out.position.w = 1.0f; return out; } diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 64410df6d..67585f180 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Metal.Effects; using SharpMetal.ObjectiveCCore; using SharpMetal.QuartzCore; using System; @@ -10,51 +11,196 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Window : IWindow, IDisposable { + public bool ScreenCaptureRequested { get; set; } + private readonly MetalRenderer _renderer; private readonly CAMetalLayer _metalLayer; + private int _width; + private int _height; + private bool _vsyncEnabled; + private AntiAliasing _currentAntiAliasing; + private bool _updateEffect; + private IPostProcessingEffect _effect; + private IScalingFilter _scalingFilter; + private bool _isLinear; + private float _scalingFilterLevel; + private bool _updateScalingFilter; + private ScalingFilter _currentScalingFilter; + private bool _colorSpacePassthroughEnabled; + public Window(MetalRenderer renderer, CAMetalLayer metalLayer) { _renderer = renderer; _metalLayer = metalLayer; } - // TODO: Handle ImageCrop public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) { var drawable = new CAMetalDrawable(ObjectiveC.IntPtr_objc_msgSend(_metalLayer, "nextDrawable")); - pipeline.Present(drawable, tex); + + _width = (int)drawable.Texture.Width; + _height = (int)drawable.Texture.Height; + + UpdateEffect(); + + if (_effect != null) + { + // TODO: Run Effects + // view = _effect.Run() + } + + int srcX0, srcX1, srcY0, srcY1; + + if (crop.Left == 0 && crop.Right == 0) + { + srcX0 = 0; + srcX1 = tex.Width; + } + else + { + srcX0 = crop.Left; + srcX1 = crop.Right; + } + + if (crop.Top == 0 && crop.Bottom == 0) + { + srcY0 = 0; + srcY1 = tex.Height; + } + else + { + srcY0 = crop.Top; + srcY1 = crop.Bottom; + } + + if (ScreenCaptureRequested) + { + // TODO: Support screen captures + + ScreenCaptureRequested = false; + } + + float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY)); + float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX)); + + int dstWidth = (int)(_width * ratioX); + int dstHeight = (int)(_height * ratioY); + + int dstPaddingX = (_width - dstWidth) / 2; + int dstPaddingY = (_height - dstHeight) / 2; + + int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX; + int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX; + + int dstY0 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; + int dstY1 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; + + if (_scalingFilter != null) + { + // TODO: Run scaling filter + } + + pipeline.Present( + drawable, + tex, + new Extents2D(srcX0, srcY0, srcX1, srcY1), + new Extents2D(dstX0, dstY0, dstX1, dstY1), + _isLinear); } } public void SetSize(int width, int height) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // Ignore } public void ChangeVSyncMode(bool vsyncEnabled) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _vsyncEnabled = vsyncEnabled; } - public void SetAntiAliasing(AntiAliasing antialiasing) + public void SetAntiAliasing(AntiAliasing effect) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (_currentAntiAliasing == effect && _effect != null) + { + return; + } + + _currentAntiAliasing = effect; + + _updateEffect = true; } public void SetScalingFilter(ScalingFilter type) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (_currentScalingFilter == type && _effect != null) + { + return; + } + + _currentScalingFilter = type; + + _updateScalingFilter = true; } public void SetScalingFilterLevel(float level) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _scalingFilterLevel = level; + _updateScalingFilter = true; } - public void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled) { } + public void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled) + { + _colorSpacePassthroughEnabled = colorSpacePassThroughEnabled; + } + + private void UpdateEffect() + { + if (_updateEffect) + { + _updateEffect = false; + + switch (_currentAntiAliasing) + { + case AntiAliasing.Fxaa: + _effect?.Dispose(); + Logger.Warning?.PrintMsg(LogClass.Gpu, "FXAA not implemented for Metal backend!"); + break; + case AntiAliasing.None: + _effect?.Dispose(); + _effect = null; + break; + case AntiAliasing.SmaaLow: + case AntiAliasing.SmaaMedium: + case AntiAliasing.SmaaHigh: + case AntiAliasing.SmaaUltra: + var quality = _currentAntiAliasing - AntiAliasing.SmaaLow; + Logger.Warning?.PrintMsg(LogClass.Gpu, "SMAA not implemented for Metal backend!"); + break; + } + } + + if (_updateScalingFilter) + { + _updateScalingFilter = false; + + switch (_currentScalingFilter) + { + case ScalingFilter.Bilinear: + case ScalingFilter.Nearest: + _scalingFilter?.Dispose(); + _scalingFilter = null; + _isLinear = _currentScalingFilter == ScalingFilter.Bilinear; + break; + case ScalingFilter.Fsr: + Logger.Warning?.PrintMsg(LogClass.Gpu, "FSR not implemented for Metal backend!"); + break; + } + } + } public void Dispose() { -- 2.47.1 From c5cca8a1a3fb0c846e76b328575b26ddcfc0495b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 10:34:16 -0400 Subject: [PATCH 196/368] Cleanup present --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 2d66a36f0..699bd155f 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -174,10 +174,6 @@ namespace Ryujinx.Graphics.Metal public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear) { - EndCurrentPass(); - - SaveState(); - // TODO: Clean this up var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); var dst = new Texture(_device, this, textureInfo, drawable.Texture, 0, 0); @@ -191,8 +187,6 @@ namespace Ryujinx.Graphics.Metal _commandBuffer = _commandQueue.CommandBuffer(); - RestoreState(); - // Cleanup dst.Dispose(); } -- 2.47.1 From 43ad627d4fae5c3e9981d80e339d8a2f535ddabe Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 10:38:00 -0400 Subject: [PATCH 197/368] Implement Texture CopyTo --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 10 ++++++++++ src/Ryujinx.Graphics.Metal/Texture.cs | 8 +------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 699bd155f..504a2d5cf 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -191,6 +191,16 @@ namespace Ryujinx.Graphics.Metal dst.Dispose(); } + public void BlitColor( + ITexture src, + ITexture dst, + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter) + { + _helperShader.BlitColor(src, dst, srcRegion, dstRegion, linearFilter); + } + public void Barrier() { diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 63da29966..79699adea 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -149,13 +149,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { - // var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); - // - // if (destination is Texture destinationTexture) - // { - // - // } - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _pipeline.BlitColor(this, destination, srcRegion, dstRegion, linearFilter); } public void CopyTo(BufferRange range, int layer, int level, int stride) -- 2.47.1 From 505f830556c59543c4c97ec7e5e668905f4a4fa9 Mon Sep 17 00:00:00 2001 From: SamoZ256 <96914946+SamoZ256@users.noreply.github.com> Date: Mon, 27 May 2024 22:54:26 +0200 Subject: [PATCH 198/368] Zero vertex buffer (#17) * cast src size to float * implement zero buffers --- .../EncoderStateManager.cs | 43 ++++++++++++++++--- src/Ryujinx.Graphics.Metal/HelperShader.cs | 8 ++-- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index d3d23c12c..84afb6eb9 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -25,11 +25,22 @@ namespace Ryujinx.Graphics.Metal public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; public readonly PrimitiveTopology Topology => _currentState.Topology; - public EncoderStateManager(MTLDevice device, Pipeline pipeline) + // RGBA32F is the biggest format + private const int ZeroBufferSize = 4 * 4; + private readonly MTLBuffer _zeroBuffer; + + public unsafe EncoderStateManager(MTLDevice device, Pipeline pipeline) { _pipeline = pipeline; _renderPipelineCache = new(device); _depthStencilCache = new(device); + + // Zero buffer + byte[] zeros = new byte[ZeroBufferSize]; + fixed (byte* ptr = zeros) + { + _zeroBuffer = device.NewBuffer((IntPtr)ptr, ZeroBufferSize, MTLResourceOptions.ResourceStorageModeShared); + } } public void Dispose() @@ -661,10 +672,17 @@ namespace Ryujinx.Graphics.Metal var vertexDescriptor = new MTLVertexDescriptor(); uint indexMask = 0; - // TODO: Handle 'zero' buffers for (int i = 0; i < attribDescriptors.Length; i++) { - if (!attribDescriptors[i].IsZero) + if (attribDescriptors[i].IsZero) + { + var attrib = vertexDescriptor.Attributes.Object((ulong)i); + attrib.Format = attribDescriptors[i].Format.Convert(); + indexMask |= 1u << bufferDescriptors.Length; + attrib.BufferIndex = (ulong)bufferDescriptors.Length; + attrib.Offset = 0; + } + else { var attrib = vertexDescriptor.Attributes.Object((ulong)i); attrib.Format = attribDescriptors[i].Format.Convert(); @@ -672,10 +690,6 @@ namespace Ryujinx.Graphics.Metal attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; attrib.Offset = (ulong)attribDescriptors[i].Offset; } - else - { - // Logger.Warning?.PrintMsg(LogClass.Gpu, "Unhandled IsZero buffer!"); - } } for (int i = 0; i < bufferDescriptors.Length; i++) @@ -684,6 +698,13 @@ namespace Ryujinx.Graphics.Metal layout.Stride = (indexMask & (1u << i)) != 0 ? (ulong)bufferDescriptors[i].Stride : 0; } + // Zero buffer + if ((indexMask & (1u << bufferDescriptors.Length)) != 0) + { + var layout = vertexDescriptor.Layouts.Object((ulong)bufferDescriptors.Length); + layout.Stride = ZeroBufferSize; + } + return vertexDescriptor; } @@ -704,6 +725,14 @@ namespace Ryujinx.Graphics.Metal } } + // Zero buffer + buffers.Add(new BufferInfo + { + Handle = _zeroBuffer.NativePtr, + Offset = 0, + Index = bufferDescriptors.Length + }); + SetBuffers(renderCommandEncoder, buffers); } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 1074a82ee..5adc336f0 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -74,10 +74,10 @@ namespace Ryujinx.Graphics.Metal Span region = stackalloc float[RegionBufferSize / sizeof(float)]; - region[0] = srcRegion.X1 / src.Width; - region[1] = srcRegion.X2 / src.Width; - region[2] = srcRegion.Y1 / src.Height; - region[3] = srcRegion.Y2 / src.Height; + region[0] = srcRegion.X1 / (float)src.Width; + region[1] = srcRegion.X2 / (float)src.Width; + region[2] = srcRegion.Y1 / (float)src.Height; + region[3] = srcRegion.Y2 / (float)src.Height; if (dstRegion.X1 > dstRegion.X2) { -- 2.47.1 From 7ed45d12dbb51c02e559b6e4240cef41ebe938dd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 18:09:29 -0400 Subject: [PATCH 199/368] Make dotnet format happy --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 2 +- src/Ryujinx.Graphics.Metal/Window.cs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 54f30e985..81cc7d3bf 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Metal public EncoderState() { } - public EncoderState Clone() + public readonly EncoderState Clone() { // Certain state (like viewport and scissor) doesn't need to be cloned, as it is always reacreated when assigned to EncoderState clone = this; diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 67585f180..38ee6459b 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -18,16 +18,16 @@ namespace Ryujinx.Graphics.Metal private int _width; private int _height; - private bool _vsyncEnabled; + // private bool _vsyncEnabled; private AntiAliasing _currentAntiAliasing; private bool _updateEffect; private IPostProcessingEffect _effect; private IScalingFilter _scalingFilter; private bool _isLinear; - private float _scalingFilterLevel; + // private float _scalingFilterLevel; private bool _updateScalingFilter; private ScalingFilter _currentScalingFilter; - private bool _colorSpacePassthroughEnabled; + // private bool _colorSpacePassthroughEnabled; public Window(MetalRenderer renderer, CAMetalLayer metalLayer) { @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Metal public void ChangeVSyncMode(bool vsyncEnabled) { - _vsyncEnabled = vsyncEnabled; + // _vsyncEnabled = vsyncEnabled; } public void SetAntiAliasing(AntiAliasing effect) @@ -148,13 +148,13 @@ namespace Ryujinx.Graphics.Metal public void SetScalingFilterLevel(float level) { - _scalingFilterLevel = level; + // _scalingFilterLevel = level; _updateScalingFilter = true; } public void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled) { - _colorSpacePassthroughEnabled = colorSpacePassThroughEnabled; + // _colorSpacePassthroughEnabled = colorSpacePassThroughEnabled; } private void UpdateEffect() @@ -177,7 +177,7 @@ namespace Ryujinx.Graphics.Metal case AntiAliasing.SmaaMedium: case AntiAliasing.SmaaHigh: case AntiAliasing.SmaaUltra: - var quality = _currentAntiAliasing - AntiAliasing.SmaaLow; + // var quality = _currentAntiAliasing - AntiAliasing.SmaaLow; Logger.Warning?.PrintMsg(LogClass.Gpu, "SMAA not implemented for Metal backend!"); break; } -- 2.47.1 From a58568d036cceed3ef0869a43b3fc370949d8509 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 21:35:32 -0400 Subject: [PATCH 200/368] RenderTargetColorMasks --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 3 ++ .../EncoderStateManager.cs | 29 +++++++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 81cc7d3bf..043fb1660 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System.Collections.Generic; +using System.Linq; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -64,6 +65,8 @@ namespace Ryujinx.Graphics.Metal // Changes to attachments take recreation! public Texture DepthStencil = default; public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments]; + + public MTLColorWriteMask[] RenderTargetMasks = Enumerable.Repeat(MTLColorWriteMask.All, Constants.MaxColorAttachments).ToArray(); public BlendDescriptor?[] BlendDescriptors = new BlendDescriptor?[Constants.MaxColorAttachments]; public ColorF BlendColor = new(); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 84afb6eb9..c737c25ba 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -207,6 +207,7 @@ namespace Ryujinx.Graphics.Metal pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + pipelineAttachment.WriteMask = _currentState.RenderTargetMasks[i]; if (_currentState.BlendDescriptors[i] != null) { @@ -349,6 +350,34 @@ namespace Ryujinx.Graphics.Metal } } + public void UpdateRenderTargetColorMasks(ReadOnlySpan componentMask) + { + _currentState.RenderTargetMasks = new MTLColorWriteMask[Constants.MaxColorAttachments]; + + for (int i = 0; i < componentMask.Length; i++) + { + bool red = (componentMask[i] & (0x1 << 0)) != 0; + bool green = (componentMask[i] & (0x1 << 1)) != 0; + bool blue = (componentMask[i] & (0x1 << 2)) != 0; + bool alpha = (componentMask[i] & (0x1 << 3)) != 0; + + var mask = MTLColorWriteMask.None; + + mask |= red ? MTLColorWriteMask.Red : 0; + mask |= green ? MTLColorWriteMask.Green : 0; + mask |= blue ? MTLColorWriteMask.Blue : 0; + mask |= alpha ? MTLColorWriteMask.Alpha : 0; + + _currentState.RenderTargetMasks[i] = mask; + } + + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) { _currentState.VertexAttribs = vertexAttribs.ToArray(); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 504a2d5cf..4ff307dce 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -461,7 +461,7 @@ namespace Ryujinx.Graphics.Metal public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _encoderStateManager.UpdateRenderTargetColorMasks(componentMask); } public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) -- 2.47.1 From ac8af32744b143be1455f095173e81cab246ccb0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 21:46:43 -0400 Subject: [PATCH 201/368] Stencil Fixes --- .../EncoderStateManager.cs | 4 +--- src/Ryujinx.Graphics.Metal/FormatTable.cs | 19 +++---------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index c737c25ba..e9d615c96 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -141,9 +141,7 @@ namespace Ryujinx.Graphics.Metal depthAttachment.LoadAction = MTLLoadAction.Load; depthAttachment.StoreAction = MTLStoreAction.Store; - var unpackedFormat = FormatTable.PackedStencilToXFormat(_currentState.DepthStencil.MTLTexture.PixelFormat); - var stencilView = _currentState.DepthStencil.MTLTexture.NewTextureView(unpackedFormat); - stencilAttachment.Texture = stencilView; + stencilAttachment.Texture = _currentState.DepthStencil.MTLTexture; stencilAttachment.LoadAction = MTLLoadAction.Load; stencilAttachment.StoreAction = MTLStoreAction.Store; break; diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 3014cdafd..7cac4f7c4 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -171,29 +171,16 @@ namespace Ryujinx.Graphics.Metal { var mtlFormat = _table[(int)format]; - if (mtlFormat == MTLPixelFormat.Depth24UnormStencil8 || mtlFormat == MTLPixelFormat.Depth32FloatStencil8) + if (mtlFormat == MTLPixelFormat.Depth24UnormStencil8) { if (!MTLDevice.CreateSystemDefaultDevice().Depth24Stencil8PixelFormatSupported) { - mtlFormat = MTLPixelFormat.Depth32Float; + Logger.Error?.PrintMsg(LogClass.Gpu, "Application requested Depth24Stencil8, which is unsupported on this device!"); + mtlFormat = MTLPixelFormat.Depth32FloatStencil8; } } return mtlFormat; } - - public static MTLPixelFormat PackedStencilToXFormat(MTLPixelFormat format) - { - switch (format) - { - case MTLPixelFormat.Depth24UnormStencil8: - return MTLPixelFormat.X24Stencil8; - case MTLPixelFormat.Depth32FloatStencil8: - return MTLPixelFormat.X32Stencil8; - default: - Logger.Warning?.PrintMsg(LogClass.Gpu, $"Attempted to get stencil format for non packed format {format}!"); - return MTLPixelFormat.Invalid; - } - } } } -- 2.47.1 From 2e99df371f677f2aba5d4a966601c4e1750d26a0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 22:00:48 -0400 Subject: [PATCH 202/368] Stencil Ref Value --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 2 ++ .../EncoderStateManager.cs | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 043fb1660..2932c5e1d 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -53,6 +53,8 @@ namespace Ryujinx.Graphics.Metal public MTLStencilDescriptor BackFaceStencil = new(); public MTLStencilDescriptor FrontFaceStencil = new(); + public int BackRefValue = 0; + public int FrontRefValue = 0; public bool StencilTestEnabled = false; public PrimitiveTopology Topology = PrimitiveTopology.Triangles; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e9d615c96..b2c490ec9 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -81,6 +81,7 @@ namespace Ryujinx.Graphics.Metal SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); SetCullMode(renderCommandEncoder); SetFrontFace(renderCommandEncoder); + SetStencilRefValue(renderCommandEncoder); // Mark the other state as dirty _currentState.Dirty.MarkAll(); @@ -161,6 +162,7 @@ namespace Ryujinx.Graphics.Metal SetDepthClamp(renderCommandEncoder); SetCullMode(renderCommandEncoder); SetFrontFace(renderCommandEncoder); + SetStencilRefValue(renderCommandEncoder); SetViewports(renderCommandEncoder); SetScissors(renderCommandEncoder); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); @@ -433,6 +435,8 @@ namespace Ryujinx.Graphics.Metal _currentState.DepthStencilState = _depthStencilCache.GetOrCreate(descriptor); + UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); + // Mark dirty _currentState.Dirty.DepthStencil = true; @@ -635,6 +639,19 @@ namespace Ryujinx.Graphics.Metal } } + private void UpdateStencilRefValue(int frontRef, int backRef) + { + _currentState.FrontRefValue = frontRef; + _currentState.BackRefValue = backRef; + + // Inline update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetStencilRefValue(renderCommandEncoder); + } + } + // Inlineable public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, MTLTexture texture, MTLSamplerState sampler) { @@ -786,6 +803,11 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetFrontFacingWinding(_currentState.Winding); } + private readonly void SetStencilRefValue(MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue); + } + private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, MTLTexture[] textures, MTLSamplerState[] samplers) { for (int i = 0; i < textures.Length; i++) -- 2.47.1 From 8be6b671b890439652eb71bb109056bee6ae44c3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 22:36:59 -0400 Subject: [PATCH 203/368] Fix FragmentOutputColor Type --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 72f60c04f..c10150559 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -219,6 +219,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { IoVariable.Position => "float4", IoVariable.PointSize => "float", + IoVariable.FragmentOutputColor => GetVarTypeName(context, context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), IoVariable.FragmentOutputDepth => "float", _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) }; -- 2.47.1 From a42c66e6d5a104dcc6e266c89b69b74d780dd70a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 23:41:11 -0400 Subject: [PATCH 204/368] Sample LOD Level --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 24c07e97c..a5e695afb 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -153,6 +153,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0; bool isArray = (texOp.Type & SamplerType.Array) != 0; @@ -234,6 +235,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCall += ", " + Src(AggregateType.S32); } + if (hasLodLevel) + { + texCall += $", level({Src(coordType)})"; + } + // TODO: Support offsets texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); -- 2.47.1 From d811532a9f770cc0b2be431a528201a7d134042a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 28 May 2024 01:45:59 -0400 Subject: [PATCH 205/368] Buffer Descriptor Step Functions --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index b2c490ec9..f440a9a60 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -740,13 +740,26 @@ namespace Ryujinx.Graphics.Metal { var layout = vertexDescriptor.Layouts.Object((ulong)i); layout.Stride = (indexMask & (1u << i)) != 0 ? (ulong)bufferDescriptors[i].Stride : 0; + + if (bufferDescriptors[i].Divisor > 0) + { + layout.StepFunction = MTLVertexStepFunction.PerInstance; + layout.StepRate = (ulong)bufferDescriptors[i].Divisor; + } + else + { + layout.StepFunction = MTLVertexStepFunction.PerVertex; + layout.StepRate = 1; + } } // Zero buffer if ((indexMask & (1u << bufferDescriptors.Length)) != 0) { var layout = vertexDescriptor.Layouts.Object((ulong)bufferDescriptors.Length); - layout.Stride = ZeroBufferSize; + layout.Stride = 1; + layout.StepFunction = MTLVertexStepFunction.Constant; + layout.StepRate = 0; } return vertexDescriptor; -- 2.47.1 From 65da0569a3defc0b6584a4c99ad31aca590b6f63 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 28 May 2024 02:18:59 -0400 Subject: [PATCH 206/368] Handle stride 0 on regular buffers --- .../EncoderStateManager.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index f440a9a60..f6906d6f3 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -739,17 +739,34 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < bufferDescriptors.Length; i++) { var layout = vertexDescriptor.Layouts.Object((ulong)i); - layout.Stride = (indexMask & (1u << i)) != 0 ? (ulong)bufferDescriptors[i].Stride : 0; - if (bufferDescriptors[i].Divisor > 0) + if ((indexMask & (1u << i)) != 0) { - layout.StepFunction = MTLVertexStepFunction.PerInstance; - layout.StepRate = (ulong)bufferDescriptors[i].Divisor; + layout.Stride = (ulong)bufferDescriptors[i].Stride; + + if (layout.Stride == 0) + { + layout.Stride = 1; + layout.StepFunction = MTLVertexStepFunction.Constant; + layout.StepRate = 0; + } + else + { + if (bufferDescriptors[i].Divisor > 0) + { + layout.StepFunction = MTLVertexStepFunction.PerInstance; + layout.StepRate = (ulong)bufferDescriptors[i].Divisor; + } + else + { + layout.StepFunction = MTLVertexStepFunction.PerVertex; + layout.StepRate = 1; + } + } } else { - layout.StepFunction = MTLVertexStepFunction.PerVertex; - layout.StepRate = 1; + layout.Stride = 0; } } -- 2.47.1 From f00cf8704f3c344d45139f2c43ddaee04c37cf16 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Wed, 29 May 2024 16:21:59 +0100 Subject: [PATCH 207/368] Metal: Compute Shaders (#19) * check for too bix texture bindings * implement lod query * print shader stage name * always have fragment input * resolve merge conflicts * fix: lod query * fix: casting texture coords * support non-array memories * use structure types for buffers * implement compute pipeline cache * compute dispatch * improve error message * rebind compute state * bind compute textures * pass local size as an argument to dispatch * implement texture buffers * hack: change vertex index to vertex id * pass support buffer as an argument to every function * return at the end of function * fix: certain missing compute bindings * implement texture base * improve texture binding system * remove useless exception * move texture handle to texture base * fix: segfault when using disposed textures --------- Co-authored-by: Samuliak Co-authored-by: SamoZ256 <96914946+SamoZ256@users.noreply.github.com> --- src/Ryujinx.Graphics.GAL/IPipeline.cs | 2 +- .../Commands/DispatchComputeCommand.cs | 10 +- .../Multithreading/ThreadedPipeline.cs | 4 +- .../Engine/Compute/ComputeClass.cs | 2 +- .../Threed/ComputeDraw/VtgAsComputeState.cs | 10 +- .../ComputePipelineCache.cs | 36 ++ src/Ryujinx.Graphics.Metal/Constants.cs | 2 +- src/Ryujinx.Graphics.Metal/EncoderState.cs | 23 +- .../EncoderStateManager.cs | 331 ++++++++++++++---- src/Ryujinx.Graphics.Metal/HelperShader.cs | 2 + src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 7 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 53 ++- src/Ryujinx.Graphics.Metal/Program.cs | 4 +- src/Ryujinx.Graphics.Metal/Texture.cs | 83 ++--- src/Ryujinx.Graphics.Metal/TextureBase.cs | 59 ++++ src/Ryujinx.Graphics.Metal/TextureBuffer.cs | 112 ++++++ src/Ryujinx.Graphics.OpenGL/Pipeline.cs | 2 +- .../CodeGen/Msl/CodeGenContext.cs | 3 + .../CodeGen/Msl/Declarations.cs | 43 ++- .../CodeGen/Msl/Instructions/InstGen.cs | 2 +- .../CodeGen/Msl/Instructions/InstGenCall.cs | 11 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 78 ++++- .../CodeGen/Msl/Instructions/IoMap.cs | 2 +- .../CodeGen/Msl/MslGenerator.cs | 49 ++- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 10 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 2 +- 26 files changed, 718 insertions(+), 224 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs create mode 100644 src/Ryujinx.Graphics.Metal/TextureBase.cs create mode 100644 src/Ryujinx.Graphics.Metal/TextureBuffer.cs diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs index b8409a573..08533ceaa 100644 --- a/src/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.GAL void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size); - void DispatchCompute(int groupsX, int groupsY, int groupsZ); + void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ); void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance); void DrawIndexed( diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs index 65028378f..36e0d836a 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs @@ -6,17 +6,23 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands private int _groupsX; private int _groupsY; private int _groupsZ; + private int _groupSizeX; + private int _groupSizeY; + private int _groupSizeZ; - public void Set(int groupsX, int groupsY, int groupsZ) + public void Set(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) { _groupsX = groupsX; _groupsY = groupsY; _groupsZ = groupsZ; + _groupSizeX = groupSizeX; + _groupSizeY = groupSizeY; + _groupSizeZ = groupSizeZ; } public static void Run(ref DispatchComputeCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.DispatchCompute(command._groupsX, command._groupsY, command._groupsZ); + renderer.Pipeline.DispatchCompute(command._groupsX, command._groupsY, command._groupsZ, command._groupSizeX, command._groupSizeY, command._groupSizeZ); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index deec36648..509954faf 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -63,9 +63,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) { - _renderer.New().Set(groupsX, groupsY, groupsZ); + _renderer.New().Set(groupsX, groupsY, groupsZ, groupSizeX, groupSizeY, groupSizeZ); _renderer.QueueCommand(); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index cd8144724..98c0ffa20 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute _channel.BufferManager.CommitComputeBindings(); - _context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth); + _context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth, qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2); _3dEngine.ForceShaderUpdate(); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 73682866b..16ae83e6f 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -211,7 +211,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.Pipeline.DispatchCompute( BitUtils.DivRoundUp(_count, ComputeLocalSize), BitUtils.DivRoundUp(_instanceCount, ComputeLocalSize), - 1); + 1, + ComputeLocalSize, + ComputeLocalSize, + ComputeLocalSize); } /// @@ -260,7 +263,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.Pipeline.DispatchCompute( BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize), BitUtils.DivRoundUp(_instanceCount, ComputeLocalSize), - _geometryAsCompute.Info.ThreadsPerInputPrimitive); + _geometryAsCompute.Info.ThreadsPerInputPrimitive, + ComputeLocalSize, + ComputeLocalSize, + ComputeLocalSize); } /// diff --git a/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs b/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs new file mode 100644 index 000000000..c35b580eb --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs @@ -0,0 +1,36 @@ +using Ryujinx.Common.Logging; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class ComputePipelineCache : StateCache + { + private readonly MTLDevice _device; + + public ComputePipelineCache(MTLDevice device) + { + _device = device; + } + + protected override MTLFunction GetHash(MTLFunction function) + { + return function; + } + + protected override MTLComputePipelineState CreateValue(MTLFunction function) + { + var error = new NSError(IntPtr.Zero); + var pipelineState = _device.NewComputePipelineState(function, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + return pipelineState; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index c9af5deaf..a413fcce1 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -15,6 +15,6 @@ namespace Ryujinx.Graphics.Metal // TODO: Check this value public const int MaxVertexLayouts = 16; public const int MaxTextures = 31; - public const int MaxSamplers = 31; + public const int MaxSamplers = 16; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 2932c5e1d..bc744ba24 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -8,20 +8,23 @@ namespace Ryujinx.Graphics.Metal { public struct DirtyFlags { - public bool Pipeline = false; + public bool RenderPipeline = false; + public bool ComputePipeline = false; public bool DepthStencil = false; public DirtyFlags() { } public void MarkAll() { - Pipeline = true; + RenderPipeline = true; + ComputePipeline = true; DepthStencil = true; } public void Clear() { - Pipeline = false; + RenderPipeline = false; + ComputePipeline = false; DepthStencil = false; } } @@ -31,13 +34,17 @@ namespace Ryujinx.Graphics.Metal { public MTLFunction? VertexFunction = null; public MTLFunction? FragmentFunction = null; + public MTLFunction? ComputeFunction = null; - public MTLTexture[] FragmentTextures = new MTLTexture[Constants.MaxTextures]; + public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTextures]; public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxSamplers]; - public MTLTexture[] VertexTextures = new MTLTexture[Constants.MaxTextures]; + public TextureBase[] VertexTextures = new TextureBase[Constants.MaxTextures]; public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxSamplers]; + public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTextures]; + public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxSamplers]; + public List UniformBuffers = []; public List StorageBuffers = []; @@ -87,10 +94,12 @@ namespace Ryujinx.Graphics.Metal { // Certain state (like viewport and scissor) doesn't need to be cloned, as it is always reacreated when assigned to EncoderState clone = this; - clone.FragmentTextures = (MTLTexture[])FragmentTextures.Clone(); + clone.FragmentTextures = (TextureBase[])FragmentTextures.Clone(); clone.FragmentSamplers = (MTLSamplerState[])FragmentSamplers.Clone(); - clone.VertexTextures = (MTLTexture[])VertexTextures.Clone(); + clone.VertexTextures = (TextureBase[])VertexTextures.Clone(); clone.VertexSamplers = (MTLSamplerState[])VertexSamplers.Clone(); + clone.ComputeTextures = (TextureBase[])ComputeTextures.Clone(); + clone.ComputeSamplers = (MTLSamplerState[])ComputeSamplers.Clone(); clone.BlendDescriptors = (BlendDescriptor?[])BlendDescriptors.Clone(); clone.VertexBuffers = (VertexBufferDescriptor[])VertexBuffers.Clone(); clone.VertexAttribs = (VertexAttribDescriptor[])VertexAttribs.Clone(); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index f6906d6f3..feaed6f44 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Metal private readonly Pipeline _pipeline; private readonly RenderPipelineCache _renderPipelineCache; + private readonly ComputePipelineCache _computePipelineCache; private readonly DepthStencilCache _depthStencilCache; private EncoderState _currentState = new(); @@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Metal { _pipeline = pipeline; _renderPipelineCache = new(device); + _computePipelineCache = new(device); _depthStencilCache = new(device); // Zero buffer @@ -50,6 +52,7 @@ namespace Ryujinx.Graphics.Metal _currentState.BackFaceStencil.Dispose(); _renderPipelineCache.Dispose(); + _computePipelineCache.Dispose(); _depthStencilCache.Dispose(); } @@ -77,8 +80,8 @@ namespace Ryujinx.Graphics.Metal SetScissors(renderCommandEncoder); SetViewports(renderCommandEncoder); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); SetCullMode(renderCommandEncoder); SetFrontFace(renderCommandEncoder); SetStencilRefValue(renderCommandEncoder); @@ -107,7 +110,7 @@ namespace Ryujinx.Graphics.Metal if (_currentState.RenderTargets[i] != null) { var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); - passAttachment.Texture = _currentState.RenderTargets[i].MTLTexture; + passAttachment.Texture = _currentState.RenderTargets[i].GetHandle(); passAttachment.LoadAction = _currentState.ClearLoadAction ? MTLLoadAction.Clear : MTLLoadAction.Load; passAttachment.StoreAction = MTLStoreAction.Store; } @@ -118,19 +121,19 @@ namespace Ryujinx.Graphics.Metal if (_currentState.DepthStencil != null) { - switch (_currentState.DepthStencil.MTLTexture.PixelFormat) + switch (_currentState.DepthStencil.GetHandle().PixelFormat) { // Depth Only Attachment case MTLPixelFormat.Depth16Unorm: case MTLPixelFormat.Depth32Float: - depthAttachment.Texture = _currentState.DepthStencil.MTLTexture; + depthAttachment.Texture = _currentState.DepthStencil.GetHandle(); depthAttachment.LoadAction = MTLLoadAction.Load; depthAttachment.StoreAction = MTLStoreAction.Store; break; // Stencil Only Attachment case MTLPixelFormat.Stencil8: - stencilAttachment.Texture = _currentState.DepthStencil.MTLTexture; + stencilAttachment.Texture = _currentState.DepthStencil.GetHandle(); stencilAttachment.LoadAction = MTLLoadAction.Load; stencilAttachment.StoreAction = MTLStoreAction.Store; break; @@ -138,16 +141,16 @@ namespace Ryujinx.Graphics.Metal // Combined Attachment case MTLPixelFormat.Depth24UnormStencil8: case MTLPixelFormat.Depth32FloatStencil8: - depthAttachment.Texture = _currentState.DepthStencil.MTLTexture; + depthAttachment.Texture = _currentState.DepthStencil.GetHandle(); depthAttachment.LoadAction = MTLLoadAction.Load; depthAttachment.StoreAction = MTLStoreAction.Store; - stencilAttachment.Texture = _currentState.DepthStencil.MTLTexture; + stencilAttachment.Texture = _currentState.DepthStencil.GetHandle(); stencilAttachment.LoadAction = MTLLoadAction.Load; stencilAttachment.StoreAction = MTLStoreAction.Store; break; default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.MTLTexture.PixelFormat}!"); + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.GetHandle().PixelFormat}!"); break; } } @@ -166,10 +169,18 @@ namespace Ryujinx.Graphics.Metal SetViewports(renderCommandEncoder); SetScissors(renderCommandEncoder); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); - SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + for (ulong i = 0; i < Constants.MaxTextures; i++) + { + SetRenderTexture(renderCommandEncoder, ShaderStage.Vertex, i, _currentState.VertexTextures[i]); + SetRenderTexture(renderCommandEncoder, ShaderStage.Fragment, i, _currentState.FragmentTextures[i]); + } + for (ulong i = 0; i < Constants.MaxSamplers; i++) + { + SetRenderSampler(renderCommandEncoder, ShaderStage.Vertex, i, _currentState.VertexSamplers[i]); + SetRenderSampler(renderCommandEncoder, ShaderStage.Fragment, i, _currentState.FragmentSamplers[i]); + } // Cleanup renderPassDescriptor.Dispose(); @@ -177,11 +188,34 @@ namespace Ryujinx.Graphics.Metal return renderCommandEncoder; } - public void RebindState(MTLRenderCommandEncoder renderCommandEncoder) + public MTLComputeCommandEncoder CreateComputeCommandEncoder() { - if (_currentState.Dirty.Pipeline) + var descriptor = new MTLComputePassDescriptor(); + var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); + + // Rebind all the state + SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers); + SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); + for (ulong i = 0; i < Constants.MaxTextures; i++) { - SetPipelineState(renderCommandEncoder); + SetComputeTexture(computeCommandEncoder, i, _currentState.ComputeTextures[i]); + } + for (ulong i = 0; i < Constants.MaxSamplers; i++) + { + SetComputeSampler(computeCommandEncoder, i, _currentState.ComputeSamplers[i]); + } + + // Cleanup + descriptor.Dispose(); + + return computeCommandEncoder; + } + + public void RebindRenderState(MTLRenderCommandEncoder renderCommandEncoder) + { + if (_currentState.Dirty.RenderPipeline) + { + SetRenderPipelineState(renderCommandEncoder); } if (_currentState.Dirty.DepthStencil) @@ -190,10 +224,22 @@ namespace Ryujinx.Graphics.Metal } // Clear the dirty flags - _currentState.Dirty.Clear(); + _currentState.Dirty.RenderPipeline = false; + _currentState.Dirty.DepthStencil = false; } - private readonly void SetPipelineState(MTLRenderCommandEncoder renderCommandEncoder) + public void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) + { + if (_currentState.Dirty.ComputePipeline) + { + SetComputePipelineState(computeCommandEncoder); + } + + // Clear the dirty flags + _currentState.Dirty.ComputePipeline = false; + } + + private readonly void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); @@ -202,7 +248,7 @@ namespace Ryujinx.Graphics.Metal if (_currentState.RenderTargets[i] != null) { var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].MTLTexture.PixelFormat; + pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].GetHandle().PixelFormat; pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; @@ -225,27 +271,27 @@ namespace Ryujinx.Graphics.Metal if (_currentState.DepthStencil != null) { - switch (_currentState.DepthStencil.MTLTexture.PixelFormat) + switch (_currentState.DepthStencil.GetHandle().PixelFormat) { // Depth Only Attachment case MTLPixelFormat.Depth16Unorm: case MTLPixelFormat.Depth32Float: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.MTLTexture.PixelFormat; + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat; break; // Stencil Only Attachment case MTLPixelFormat.Stencil8: - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.MTLTexture.PixelFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat; break; // Combined Attachment case MTLPixelFormat.Depth24UnormStencil8: case MTLPixelFormat.Depth32FloatStencil8: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.MTLTexture.PixelFormat; - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.MTLTexture.PixelFormat; + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat; break; default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.MTLTexture.PixelFormat}!"); + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.GetHandle().PixelFormat}!"); break; } } @@ -287,6 +333,18 @@ namespace Ryujinx.Graphics.Metal } } + private readonly void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) + { + if (_currentState.ComputeFunction == null) + { + return; + } + + var pipelineState = _computePipelineCache.GetOrCreate(_currentState.ComputeFunction.Value); + + computeCommandEncoder.SetComputePipelineState(pipelineState); + } + public void UpdateIndexBuffer(BufferRange buffer, IndexType type) { if (buffer.Handle != BufferHandle.Null) @@ -307,17 +365,34 @@ namespace Ryujinx.Graphics.Metal { Program prg = (Program)program; - if (prg.VertexFunction == IntPtr.Zero) + if (prg.VertexFunction == IntPtr.Zero && prg.ComputeFunction == IntPtr.Zero) { - Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!"); + if (prg.FragmentFunction == IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, "No compute function"); + } + else + { + Logger.Error?.PrintMsg(LogClass.Gpu, "No vertex function"); + } return; } - _currentState.VertexFunction = prg.VertexFunction; - _currentState.FragmentFunction = prg.FragmentFunction; + if (prg.VertexFunction != IntPtr.Zero) + { + _currentState.VertexFunction = prg.VertexFunction; + _currentState.FragmentFunction = prg.FragmentFunction; - // Mark dirty - _currentState.Dirty.Pipeline = true; + // Mark dirty + _currentState.Dirty.RenderPipeline = true; + } + if (prg.ComputeFunction != IntPtr.Zero) + { + _currentState.ComputeFunction = prg.ComputeFunction; + + // Mark dirty + _currentState.Dirty.ComputePipeline = true; + } } public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) @@ -383,7 +458,7 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexAttribs = vertexAttribs.ToArray(); // Mark dirty - _currentState.Dirty.Pipeline = true; + _currentState.Dirty.RenderPipeline = true; } public void UpdateBlendDescriptors(int index, BlendDescriptor blend) @@ -557,7 +632,7 @@ namespace Ryujinx.Graphics.Metal } // Mark dirty - _currentState.Dirty.Pipeline = true; + _currentState.Dirty.RenderPipeline = true; } // Inlineable @@ -579,10 +654,18 @@ namespace Ryujinx.Graphics.Metal } // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.CurrentEncoder != null) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + } + else if (_pipeline.CurrentEncoderType == EncoderType.Compute) + { + var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); + SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers); + } } } @@ -606,10 +689,18 @@ namespace Ryujinx.Graphics.Metal } // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.CurrentEncoder != null) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + } + else if (_pipeline.CurrentEncoderType == EncoderType.Compute) + { + var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); + SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); + } } } @@ -653,29 +744,86 @@ namespace Ryujinx.Graphics.Metal } // Inlineable - public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, MTLTexture texture, MTLSamplerState sampler) + public readonly void UpdateTexture(ShaderStage stage, ulong binding, TextureBase texture) { + if (binding > 30) + { + Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= 30"); + return; + } switch (stage) { case ShaderStage.Fragment: _currentState.FragmentTextures[binding] = texture; - _currentState.FragmentSamplers[binding] = sampler; break; case ShaderStage.Vertex: _currentState.VertexTextures[binding] = texture; - _currentState.VertexSamplers[binding] = sampler; + break; + case ShaderStage.Compute: + _currentState.ComputeTextures[binding] = texture; break; } - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.CurrentEncoder != null) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - // TODO: Only update the new ones - SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); - SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetRenderTexture(renderCommandEncoder, ShaderStage.Vertex, binding, texture); + SetRenderTexture(renderCommandEncoder, ShaderStage.Fragment, binding, texture); + } + else if (_pipeline.CurrentEncoderType == EncoderType.Compute) + { + var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); + SetComputeTexture(computeCommandEncoder, binding, texture); + } } } + // Inlineable + public readonly void UpdateSampler(ShaderStage stage, ulong binding, MTLSamplerState sampler) + { + if (binding > 15) + { + Logger.Warning?.Print(LogClass.Gpu, $"Sampler binding ({binding}) must be <= 15"); + return; + } + switch (stage) + { + case ShaderStage.Fragment: + _currentState.FragmentSamplers[binding] = sampler; + break; + case ShaderStage.Vertex: + _currentState.VertexSamplers[binding] = sampler; + break; + case ShaderStage.Compute: + _currentState.ComputeSamplers[binding] = sampler; + break; + } + + if (_pipeline.CurrentEncoder != null) + { + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetRenderSampler(renderCommandEncoder, ShaderStage.Vertex, binding, sampler); + SetRenderSampler(renderCommandEncoder, ShaderStage.Fragment, binding, sampler); + } + else if (_pipeline.CurrentEncoderType == EncoderType.Compute) + { + var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); + SetComputeSampler(computeCommandEncoder, binding, sampler); + } + } + } + + // Inlineable + public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, MTLSamplerState sampler) + { + UpdateTexture(stage, binding, texture); + UpdateSampler(stage, binding, sampler); + } + private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { if (_currentState.DepthStencilState != null) @@ -807,10 +955,10 @@ namespace Ryujinx.Graphics.Metal Index = bufferDescriptors.Length }); - SetBuffers(renderCommandEncoder, buffers); + SetRenderBuffers(renderCommandEncoder, buffers); } - private readonly void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) + private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) { foreach (var buffer in buffers) { @@ -823,6 +971,14 @@ namespace Ryujinx.Graphics.Metal } } + private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, List buffers) + { + foreach (var buffer in buffers) + { + computeCommandEncoder.SetBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index); + } + } + private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) { renderCommandEncoder.SetCullMode(_currentState.CullMode); @@ -838,41 +994,64 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue); } - private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, MTLTexture[] textures, MTLSamplerState[] samplers) + private static void SetRenderTexture(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, ulong binding, TextureBase texture) { - for (int i = 0; i < textures.Length; i++) + if (texture == null) { - var texture = textures[i]; - if (texture != IntPtr.Zero) - { - switch (stage) - { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexTexture(texture, (ulong)i); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentTexture(texture, (ulong)i); - break; - } - } + return; } - for (int i = 0; i < samplers.Length; i++) + var textureHandle = texture.GetHandle(); + if (textureHandle != IntPtr.Zero) { - var sampler = samplers[i]; - if (sampler != IntPtr.Zero) + switch (stage) { - switch (stage) - { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexSamplerState(sampler, (ulong)i); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentSamplerState(sampler, (ulong)i); - break; - } + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexTexture(textureHandle, binding); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentTexture(textureHandle, binding); + break; } } } + + private static void SetRenderSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, ulong binding, MTLSamplerState sampler) + { + if (sampler != IntPtr.Zero) + { + switch (stage) + { + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexSamplerState(sampler, binding); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentSamplerState(sampler, binding); + break; + } + } + } + + private static void SetComputeTexture(MTLComputeCommandEncoder computeCommandEncoder, ulong binding, TextureBase texture) + { + if (texture == null) + { + return; + } + + var textureHandle = texture.GetHandle(); + if (textureHandle != IntPtr.Zero) + { + computeCommandEncoder.SetTexture(textureHandle, binding); + } + } + + private static void SetComputeSampler(MTLComputeCommandEncoder computeCommandEncoder, ulong binding, MTLSamplerState sampler) + { + if (sampler != IntPtr.Zero) + { + computeCommandEncoder.SetSamplerState(sampler, binding); + } + } } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 5adc336f0..2ed9fe5aa 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -5,6 +5,8 @@ using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Metal; using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 55d25c8ae..84333e1f9 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -97,9 +97,12 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateTexture(TextureCreateInfo info) { - var texture = new Texture(_device, _pipeline, info); + if (info.Target == Target.TextureBuffer) + { + return new TextureBuffer(_device, _pipeline, info); + } - return texture; + return new Texture(_device, _pipeline, info); } public ITextureArray CreateTextureArray(int size, bool isBuffer) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 4ff307dce..34e9d5832 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -69,7 +69,6 @@ namespace Ryujinx.Graphics.Metal public MTLRenderCommandEncoder GetOrCreateRenderEncoder() { MTLRenderCommandEncoder renderCommandEncoder; - if (_currentEncoder == null || _currentEncoderType != EncoderType.Render) { renderCommandEncoder = BeginRenderPass(); @@ -79,7 +78,7 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder = new MTLRenderCommandEncoder(_currentEncoder.Value); } - _encoderStateManager.RebindState(renderCommandEncoder); + _encoderStateManager.RebindRenderState(renderCommandEncoder); return renderCommandEncoder; } @@ -99,15 +98,19 @@ namespace Ryujinx.Graphics.Metal public MTLComputeCommandEncoder GetOrCreateComputeEncoder() { - if (_currentEncoder != null) + MTLComputeCommandEncoder computeCommandEncoder; + if (_currentEncoder == null || _currentEncoderType != EncoderType.Compute) { - if (_currentEncoderType == EncoderType.Compute) - { - return new MTLComputeCommandEncoder(_currentEncoder.Value); - } + computeCommandEncoder = BeginComputePass(); + } + else + { + computeCommandEncoder = new MTLComputeCommandEncoder(_currentEncoder.Value); } - return BeginComputePass(); + _encoderStateManager.RebindComputeState(computeCommandEncoder); + + return computeCommandEncoder; } public void EndCurrentPass() @@ -164,8 +167,7 @@ namespace Ryujinx.Graphics.Metal { EndCurrentPass(); - var descriptor = new MTLComputePassDescriptor(); - var computeCommandEncoder = _commandBuffer.ComputeCommandEncoder(descriptor); + var computeCommandEncoder = _encoderStateManager.CreateComputeCommandEncoder(); _currentEncoder = computeCommandEncoder; _currentEncoderType = EncoderType.Compute; @@ -274,9 +276,13 @@ namespace Ryujinx.Graphics.Metal (ulong)size); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + var computeCommandEncoder = GetOrCreateComputeEncoder(); + + computeCommandEncoder.DispatchThreadgroups( + new MTLSize{width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ}, + new MTLSize{width = (ulong)groupSizeX, height = (ulong)groupSizeY, depth = (ulong)groupSizeZ}); } public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) @@ -397,7 +403,10 @@ namespace Ryujinx.Graphics.Metal public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (texture is TextureBase tex) + { + _encoderStateManager.UpdateTexture(stage, (ulong)binding, tex); + } } public void SetImageArray(ShaderStage stage, int binding, IImageArray array) @@ -491,28 +500,14 @@ namespace Ryujinx.Graphics.Metal public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) { - if (texture is Texture tex) + if (texture is TextureBase tex) { if (sampler is Sampler samp) { - var mtlTexture = tex.MTLTexture; var mtlSampler = samp.GetSampler(); var index = (ulong)binding; - switch (stage) - { - case ShaderStage.Vertex: - case ShaderStage.Fragment: - _encoderStateManager.UpdateTextureAndSampler(stage, index, mtlTexture, mtlSampler); - break; - case ShaderStage.Compute: - var computeCommandEncoder = GetOrCreateComputeEncoder(); - computeCommandEncoder.SetTexture(mtlTexture, index); - computeCommandEncoder.SetSamplerState(mtlSampler, index); - break; - default: - throw new ArgumentOutOfRangeException(nameof(stage), stage, "Unsupported shader stage!"); - } + _encoderStateManager.UpdateTextureAndSampler(stage, index, tex, mtlSampler); } } } diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index ee0ce4f78..8ff690463 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Metal var shaderLibrary = device.NewLibrary(StringHelper.NSString(shader.Code), new MTLCompileOptions(IntPtr.Zero), ref libraryError); if (libraryError != IntPtr.Zero) { - Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); + Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); _status = ProgramLinkStatus.Failure; return; } @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Metal switch (shaders[index].Stage) { case ShaderStage.Compute: - ComputeFunction = shaderLibrary.NewFunction(StringHelper.NSString("computeMain")); + ComputeFunction = shaderLibrary.NewFunction(StringHelper.NSString("kernelMain")); break; case ShaderStage.Vertex: VertexFunction = shaderLibrary.NewFunction(StringHelper.NSString("vertexMain")); diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 79699adea..54e037ed7 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -10,24 +10,10 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class Texture : ITexture, IDisposable + class Texture : TextureBase, ITexture { - private readonly TextureCreateInfo _info; - private readonly Pipeline _pipeline; - private readonly MTLDevice _device; - - public MTLTexture MTLTexture; - public TextureCreateInfo Info => _info; - public int Width => Info.Width; - public int Height => Info.Height; - public int Depth => Info.Depth; - - public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) + public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) : base(device, pipeline, info) { - _device = device; - _pipeline = pipeline; - _info = info; - var descriptor = new MTLTextureDescriptor { PixelFormat = FormatTable.GetFormat(Info.Format), @@ -50,15 +36,11 @@ namespace Ryujinx.Graphics.Metal descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat); - MTLTexture = _device.NewTexture(descriptor); + _mtlTexture = _device.NewTexture(descriptor); } - public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) + public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, pipeline, info) { - _device = device; - _pipeline = pipeline; - _info = info; - var pixelFormat = FormatTable.GetFormat(Info.Format); var textureType = Info.Target.Convert(); NSRange levels; @@ -75,7 +57,7 @@ namespace Ryujinx.Graphics.Metal var swizzle = GetSwizzle(info, pixelFormat); - MTLTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + _mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); } private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) @@ -118,14 +100,14 @@ namespace Ryujinx.Graphics.Metal if (destination is Texture destinationTexture) { blitCommandEncoder.CopyFromTexture( - MTLTexture, + _mtlTexture, (ulong)firstLayer, (ulong)firstLevel, - destinationTexture.MTLTexture, + destinationTexture._mtlTexture, (ulong)firstLayer, (ulong)firstLevel, - MTLTexture.ArrayLength, - MTLTexture.MipmapLevelCount); + _mtlTexture.ArrayLength, + _mtlTexture.MipmapLevelCount); } } @@ -136,14 +118,14 @@ namespace Ryujinx.Graphics.Metal if (destination is Texture destinationTexture) { blitCommandEncoder.CopyFromTexture( - MTLTexture, + _mtlTexture, (ulong)srcLayer, (ulong)srcLevel, - destinationTexture.MTLTexture, + destinationTexture._mtlTexture, (ulong)dstLayer, (ulong)dstLevel, - MTLTexture.ArrayLength, - MTLTexture.MipmapLevelCount); + _mtlTexture.ArrayLength, + _mtlTexture.MipmapLevelCount); } } @@ -158,7 +140,7 @@ namespace Ryujinx.Graphics.Metal ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; - if (MTLTexture.TextureType == MTLTextureType.Type3D) + if (_mtlTexture.TextureType == MTLTextureType.Type3D) { bytesPerImage = bytesPerRow * (ulong)Info.Height; } @@ -167,11 +149,11 @@ namespace Ryujinx.Graphics.Metal MTLBuffer mtlBuffer = new(Unsafe.As(ref handle)); blitCommandEncoder.CopyFromTexture( - MTLTexture, + _mtlTexture, (ulong)layer, (ulong)level, new MTLOrigin(), - new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height, depth = MTLTexture.Depth }, + new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, mtlBuffer, (ulong)range.Offset, bytesPerRow, @@ -180,7 +162,7 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - return new Texture(_device, _pipeline, info, MTLTexture, firstLayer, firstLevel); + return new Texture(_device, _pipeline, info, _mtlTexture, firstLayer, firstLevel); } public PinnedSpan GetData() @@ -195,7 +177,7 @@ namespace Ryujinx.Graphics.Metal ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong length = bytesPerRow * (ulong)Info.Height; ulong bytesPerImage = 0; - if (MTLTexture.TextureType == MTLTextureType.Type3D) + if (_mtlTexture.TextureType == MTLTextureType.Type3D) { bytesPerImage = length; } @@ -205,11 +187,11 @@ namespace Ryujinx.Graphics.Metal var mtlBuffer = _device.NewBuffer(length, MTLResourceOptions.ResourceStorageModeShared); blitCommandEncoder.CopyFromTexture( - MTLTexture, + _mtlTexture, (ulong)layer, (ulong)level, new MTLOrigin(), - new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height, depth = MTLTexture.Depth }, + new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, mtlBuffer, 0, bytesPerRow, @@ -255,7 +237,7 @@ namespace Ryujinx.Graphics.Metal (ulong)Info.GetMipStride(level), (ulong)mipSize, new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, - MTLTexture, + _mtlTexture, 0, (ulong)level, new MTLOrigin() @@ -282,7 +264,7 @@ namespace Ryujinx.Graphics.Metal ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; - if (MTLTexture.TextureType == MTLTextureType.Type3D) + if (_mtlTexture.TextureType == MTLTextureType.Type3D) { bytesPerImage = bytesPerRow * (ulong)Info.Height; } @@ -299,8 +281,8 @@ namespace Ryujinx.Graphics.Metal 0, bytesPerRow, bytesPerImage, - new MTLSize { width = MTLTexture.Width, height = MTLTexture.Height, depth = MTLTexture.Depth }, - MTLTexture, + new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, + _mtlTexture, (ulong)layer, (ulong)level, new MTLOrigin() @@ -317,7 +299,7 @@ namespace Ryujinx.Graphics.Metal ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; - if (MTLTexture.TextureType == MTLTextureType.Type3D) + if (_mtlTexture.TextureType == MTLTextureType.Type3D) { bytesPerImage = bytesPerRow * (ulong)Info.Height; } @@ -335,7 +317,7 @@ namespace Ryujinx.Graphics.Metal bytesPerRow, bytesPerImage, new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, - MTLTexture, + _mtlTexture, (ulong)layer, (ulong)level, new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } @@ -348,18 +330,7 @@ namespace Ryujinx.Graphics.Metal public void SetStorage(BufferRange buffer) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - } - - public void Release() - { - Dispose(); - } - - public void Dispose() - { - MTLTexture.SetPurgeableState(MTLPurgeableState.Volatile); - MTLTexture.Dispose(); + throw new NotImplementedException(); } } } diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs new file mode 100644 index 000000000..7a634553a --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -0,0 +1,59 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + abstract class TextureBase : IDisposable + { + private bool _disposed; + + protected readonly TextureCreateInfo _info; + protected readonly Pipeline _pipeline; + protected readonly MTLDevice _device; + + protected MTLTexture _mtlTexture; + + public TextureCreateInfo Info => _info; + public int Width => Info.Width; + public int Height => Info.Height; + public int Depth => Info.Depth; + + public TextureBase(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) + { + _device = device; + _pipeline = pipeline; + _info = info; + } + + public MTLTexture GetHandle() + { + if (_disposed) + { + return new MTLTexture(IntPtr.Zero); + } + + return _mtlTexture; + } + + public void Release() + { + Dispose(); + } + + public void Dispose() + { + if (_mtlTexture != IntPtr.Zero) + { + _mtlTexture.Dispose(); + } + _disposed = true; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs new file mode 100644 index 000000000..4827e95bf --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs @@ -0,0 +1,112 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + class TextureBuffer : Texture, ITexture + { + private MTLBuffer? _bufferHandle; + private int _offset; + private int _size; + + public TextureBuffer(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) : base(device, pipeline, info) { } + + public void CreateView() + { + var descriptor = new MTLTextureDescriptor + { + PixelFormat = FormatTable.GetFormat(Info.Format), + Usage = MTLTextureUsage.ShaderRead | MTLTextureUsage.ShaderWrite, + StorageMode = MTLStorageMode.Shared, + TextureType = Info.Target.Convert(), + Width = (ulong)Info.Width, + Height = (ulong)Info.Height + }; + + _mtlTexture = _bufferHandle.Value.NewTexture(descriptor, (ulong)_offset, (ulong)_size); + } + + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + throw new NotSupportedException(); + } + + public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + throw new NotSupportedException(); + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + throw new NotSupportedException(); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + throw new NotSupportedException(); + } + + // TODO: Implement this method + public PinnedSpan GetData() + { + throw new NotImplementedException(); + } + + public PinnedSpan GetData(int layer, int level) + { + return GetData(); + } + + public void CopyTo(BufferRange range, int layer, int level, int stride) + { + throw new NotImplementedException(); + } + + public void SetData(IMemoryOwner data) + { + // TODO + //_gd.SetBufferData(_bufferHandle, _offset, data.Memory.Span); + data.Dispose(); + } + + public void SetData(IMemoryOwner data, int layer, int level) + { + throw new NotSupportedException(); + } + + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) + { + throw new NotSupportedException(); + } + + public void SetStorage(BufferRange buffer) + { + if (buffer.Handle != BufferHandle.Null) + { + var handle = buffer.Handle; + MTLBuffer bufferHandle = new(Unsafe.As(ref handle)); + if (_bufferHandle == bufferHandle && + _offset == buffer.Offset && + _size == buffer.Size) + { + return; + } + + _bufferHandle = bufferHandle; + _offset = buffer.Offset; + _size = buffer.Size; + + Release(); + + CreateView(); + } + } + } +} diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index 096e2e5eb..4df9bdc19 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.OpenGL Buffer.Copy(source, destination, srcOffset, dstOffset, size); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) { if (!_program.IsLinked) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index f67e1cb3f..0b0d598c5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -8,6 +8,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { public const string Tab = " "; + // The number of additional arguments that every function (except for the main one) must have (for instance support_buffer) + public const int additionalArgCount = 1; + public StructuredFunction CurrentFunction { get; set; } public StructuredProgramInfo Info { get; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index c10150559..0e98995f8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -54,6 +54,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); context.AppendLine(); DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); + context.AppendLine(); + DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values); + DeclareBufferStructures(context, context.Properties.StorageBuffers.Values); } static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) @@ -111,8 +114,41 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { foreach (var memory in memories) { + string arraySize = ""; + if ((memory.Type & AggregateType.Array) != 0) + { + arraySize = $"[{memory.ArrayLength}]"; + } var typeName = GetVarTypeName(context, memory.Type & ~AggregateType.Array); - context.AppendLine($"{typeName} {memory.Name}[{memory.ArrayLength}];"); + context.AppendLine($"{typeName} {memory.Name}{arraySize};"); + } + } + + private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers) + { + foreach (BufferDefinition buffer in buffers) + { + context.AppendLine($"struct Struct_{buffer.Name}"); + context.EnterScope(); + + foreach (StructureField field in buffer.Type.Fields) + { + if (field.Type.HasFlag(AggregateType.Array) && field.ArrayLength > 0) + { + string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); + + context.AppendLine($"{typeName} {field.Name}[{field.ArrayLength}];"); + } + else + { + string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); + + context.AppendLine($"{typeName} {field.Name};"); + } + } + + context.LeaveScope(";"); + context.AppendLine(); } } @@ -124,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else { - if (inputs.Any()) + if (inputs.Any() || context.Definitions.Stage == ShaderStage.Fragment) { string prefix = ""; @@ -136,9 +172,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl case ShaderStage.Fragment: context.AppendLine($"struct FragmentIn"); break; - case ShaderStage.Compute: - context.AppendLine($"struct KernelIn"); - break; } context.EnterScope(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index f18b34597..d35b5c9f7 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.Load: return Load(context, operation); case Instruction.Lod: - return "|| LOD ||"; + return Lod(context, operation); case Instruction.MemoryBarrier: return "|| MEMORY BARRIER ||"; case Instruction.Store: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs index df9d10301..f233908c4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs @@ -12,11 +12,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions var functon = context.GetFunction(funcId.Value); - string[] args = new string[operation.SourcesCount - 1]; + int argCount = operation.SourcesCount - 1; + string[] args = new string[argCount + CodeGenContext.additionalArgCount]; - for (int i = 0; i < args.Length; i++) + // Additional arguments + args[0] = "support_buffer"; + + int argIndex = CodeGenContext.additionalArgCount; + for (int i = 0; i < argCount; i++) { - args[i] = GetSourceExpr(context, operation.GetSource(i + 1), functon.GetArgumentType(i)); + args[argIndex++] = GetSourceExpr(context, operation.GetSource(i + 1), functon.GetArgumentType(i)); } return $"{functon.Name}({string.Join(", ", args)})"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index a5e695afb..7f43717e0 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions inputsCount--; } + string fieldName = ""; switch (storageKind) { case StorageKind.ConstantBuffer: @@ -45,6 +46,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions StructureField field = buffer.Type.Fields[fieldIndex.Value]; varName = buffer.Name; + if ((field.Type & AggregateType.Array) != 0 && field.ArrayLength == 0) + { + // Unsized array, the buffer is indexed instead of the field + fieldName = "." + field.Name; + } + else + { + varName += "->" + field.Name; + } varType = field.Type; break; @@ -126,6 +136,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions varName += $"[{GetSourceExpr(context, src, AggregateType.S32)}]"; } } + varName += fieldName; if (isStore) { @@ -141,6 +152,37 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return GenerateLoadOrStore(context, operation, isStore: false); } + // TODO: check this + public static string Lod(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + int coordsCount = texOp.Type.GetDimensions(); + int coordsIndex = 0; + + string samplerName = GetSamplerName(context.Properties, texOp); + + string coordsExpr; + + if (coordsCount > 1) + { + string[] elems = new string[coordsCount]; + + for (int index = 0; index < coordsCount; index++) + { + elems[index] = GetSourceExpr(context, texOp.GetSource(coordsIndex + index), AggregateType.FP32); + } + + coordsExpr = "float" + coordsCount + "(" + string.Join(", ", elems) + ")"; + } + else + { + coordsExpr = GetSourceExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32); + } + + return $"tex_{samplerName}.calculate_unclamped_lod(samp_{samplerName}, {coordsExpr}){GetMaskMultiDest(texOp.Index)}"; + } + public static string Store(CodeGenContext context, AstOperation operation) { return GenerateLoadOrStore(context, operation, isStore: true); @@ -176,11 +218,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } else { - texCall += "sample"; - if (isGather) { - texCall += "_gather"; + texCall += "gather"; + } + else + { + texCall += "sample"; } if (isShadow) @@ -188,22 +232,31 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCall += "_compare"; } - texCall += $"(samp_{samplerName}"; + texCall += $"(samp_{samplerName}, "; } int coordsCount = texOp.Type.GetDimensions(); int pCount = coordsCount; + bool appended = false; void Append(string str) { - texCall += ", " + str; + if (appended) + { + texCall += ", "; + } + else { + appended = true; + } + texCall += str; } AggregateType coordType = intCoords ? AggregateType.S32 : AggregateType.FP32; string AssemblePVector(int count) { + string coords; if (count > 1) { string[] elems = new string[count]; @@ -213,14 +266,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions elems[index] = Src(coordType); } - string prefix = intCoords ? "int" : "float"; - - return prefix + count + "(" + string.Join(", ", elems) + ")"; + coords = string.Join(", ", elems); } else { - return Src(coordType); + coords = Src(coordType); } + + string prefix = intCoords ? "uint" : "float"; + + return prefix + (count > 1 ? count : "") + "(" + coords + ")"; } Append(AssemblePVector(pCount)); @@ -254,6 +309,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions private static string GetMaskMultiDest(int mask) { + if (mask == 0x0) + { + return ""; + } + string swizzle = "."; for (int i = 0; i < 4; i++) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index b98db242d..b306e8283 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.ThreadId => ("thread_position_in_threadgroup", AggregateType.Vector3 | AggregateType.U32), IoVariable.VertexId => ("vertex_id", AggregateType.S32), // gl_VertexIndex does not have a direct equivalent in MSL - IoVariable.VertexIndex => ("vertex_index", AggregateType.U32), + IoVariable.VertexIndex => ("vertex_id", AggregateType.U32), IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), IoVariable.FragmentCoord => ("in.position", AggregateType.Vector4 | AggregateType.FP32), _ => (null, AggregateType.Invalid), diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 18953943e..2866574eb 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -48,6 +48,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl PrintBlock(context, function.MainBlock, isMainFunc); + // In case the shader hasn't returned, return + if (isMainFunc && stage != ShaderStage.Compute) + { + context.AppendLine("return out;"); + } + context.LeaveScope(); } @@ -57,11 +63,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl ShaderStage stage, bool isMainFunc = false) { - string[] args = new string[function.InArguments.Length + function.OutArguments.Length]; + int additionalArgCount = isMainFunc ? 0 : CodeGenContext.additionalArgCount; + string[] args = new string[additionalArgCount + function.InArguments.Length + function.OutArguments.Length]; + + // All non-main functions need to be able to access the support_buffer as well + if (!isMainFunc) + { + args[0] = "constant Struct_support_buffer* support_buffer"; + } + + int argIndex = additionalArgCount; for (int i = 0; i < function.InArguments.Length; i++) { - args[i] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; + args[argIndex++] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; } for (int i = 0; i < function.OutArguments.Length; i++) @@ -69,7 +84,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl int j = i + function.InArguments.Length; // Likely need to be made into pointers - args[j] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; + args[argIndex++] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; } string funcKeyword = "inline"; @@ -97,20 +112,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl returnType = "void"; } - if (context.AttributeUsage.UsedInputAttributes != 0) + if (stage == ShaderStage.Vertex) { - if (stage == ShaderStage.Vertex) + if (context.AttributeUsage.UsedInputAttributes != 0) { args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); } - else if (stage == ShaderStage.Fragment) - { - args = args.Prepend("FragmentIn in [[stage_in]]").ToArray(); - } - else if (stage == ShaderStage.Compute) - { - args = args.Prepend("KernelIn in [[stage_in]]").ToArray(); - } + } + else if (stage == ShaderStage.Fragment) + { + args = args.Prepend("FragmentIn in [[stage_in]]").ToArray(); } // TODO: add these only if they are used @@ -119,18 +130,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl args = args.Append("uint vertex_id [[vertex_id]]").ToArray(); args = args.Append("uint instance_id [[instance_id]]").ToArray(); } + else if (stage == ShaderStage.Compute) + { + args = args.Append("uint3 threadgroup_position_in_grid [[threadgroup_position_in_grid]]").ToArray(); + args = args.Append("uint3 thread_position_in_grid [[thread_position_in_grid]]").ToArray(); + args = args.Append("uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]]").ToArray(); + } foreach (var constantBuffer in context.Properties.ConstantBuffers.Values) { - var varType = constantBuffer.Type.Fields[0].Type & ~AggregateType.Array; - args = args.Append($"constant {Declarations.GetVarTypeName(context, varType)} *{constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); + args = args.Append($"constant Struct_{constantBuffer.Name}* {constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); } foreach (var storageBuffers in context.Properties.StorageBuffers.Values) { - var varType = storageBuffers.Type.Fields[0].Type & ~AggregateType.Array; // Offset the binding by 15 to avoid clashing with the constant buffers - args = args.Append($"device {Declarations.GetVarTypeName(context, varType)} *{storageBuffers.Name} [[buffer({storageBuffers.Binding + 15})]]").ToArray(); + args = args.Append($"device Struct_{storageBuffers.Name}* {storageBuffers.Name} [[buffer({storageBuffers.Binding + 15})]]").ToArray(); } foreach (var texture in context.Properties.Textures.Values) diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index b7c42aff0..0243dda40 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -861,7 +861,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programStrideChange); - _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1); + _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 0, 0, 0); _pipeline.Finish(gd, cbs); } @@ -1044,7 +1044,7 @@ namespace Ryujinx.Graphics.Vulkan int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32; - _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1, 0, 0, 0); if (srcView != src) { @@ -1170,7 +1170,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(format)); - _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1, 0, 0, 0); if (srcView != src) { @@ -1582,7 +1582,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(3, patternScoped.Range) }); _pipeline.SetProgram(_programConvertIndirectData); - _pipeline.DispatchCompute(1, 1, 1); + _pipeline.DispatchCompute(1, 1, 1, 0, 0, 0); BufferHolder.InsertBufferBarrier( gd, @@ -1684,7 +1684,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programConvertD32S8ToD24S8); - _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1); + _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1, 0, 0, 0); _pipeline.Finish(gd, cbs); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index addad83fd..c74c87e8d 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -295,7 +295,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) { if (!_program.IsLinked) { -- 2.47.1 From 49e83335d17e8c687561e98146bd125332bbee3b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 16:24:49 +0100 Subject: [PATCH 208/368] Cleanup + Format --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 7 ------- src/Ryujinx.Graphics.Metal/HelperShader.cs | 2 -- src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 ++-- src/Ryujinx.Graphics.Metal/Texture.cs | 1 - src/Ryujinx.Graphics.Metal/TextureBase.cs | 4 ---- src/Ryujinx.Graphics.Metal/TextureBuffer.cs | 2 -- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 3 ++- 7 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index bc744ba24..a75ea3dc9 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -20,13 +20,6 @@ namespace Ryujinx.Graphics.Metal ComputePipeline = true; DepthStencil = true; } - - public void Clear() - { - RenderPipeline = false; - ComputePipeline = false; - DepthStencil = false; - } } [SupportedOSPlatform("macos")] diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 2ed9fe5aa..5adc336f0 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -5,8 +5,6 @@ using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Metal; using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 34e9d5832..4d36893d4 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -281,8 +281,8 @@ namespace Ryujinx.Graphics.Metal var computeCommandEncoder = GetOrCreateComputeEncoder(); computeCommandEncoder.DispatchThreadgroups( - new MTLSize{width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ}, - new MTLSize{width = (ulong)groupSizeX, height = (ulong)groupSizeY, depth = (ulong)groupSizeZ}); + new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ }, + new MTLSize { width = (ulong)groupSizeX, height = (ulong)groupSizeY, depth = (ulong)groupSizeZ }); } public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 54e037ed7..6692d6133 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using SharpMetal.Foundation; using SharpMetal.Metal; diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index 7a634553a..8bd5c9513 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -1,10 +1,6 @@ -using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; using SharpMetal.Metal; using System; -using System.Buffers; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs index 4827e95bf..38468a76c 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs @@ -1,6 +1,4 @@ -using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; using SharpMetal.Metal; using System; using System.Buffers; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 7f43717e0..88814f5c0 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -246,7 +246,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { texCall += ", "; } - else { + else + { appended = true; } texCall += str; -- 2.47.1 From 9ebf82f184f942e0373d32ea276e10c682afdf34 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 16:31:49 +0100 Subject: [PATCH 209/368] More cleanup --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 2 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index feaed6f44..eef8fd341 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -188,7 +188,7 @@ namespace Ryujinx.Graphics.Metal return renderCommandEncoder; } - public MTLComputeCommandEncoder CreateComputeCommandEncoder() + public readonly MTLComputeCommandEncoder CreateComputeCommandEncoder() { var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 88814f5c0..5a8152db7 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -283,17 +283,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions if (isArray) { - texCall += ", " + Src(AggregateType.S32); + Append(Src(AggregateType.S32)); } if (isShadow) { - texCall += ", " + Src(AggregateType.S32); + Append(Src(AggregateType.S32)); } if (hasLodLevel) { - texCall += $", level({Src(coordType)})"; + Append("level({Src(coordType)})"); } // TODO: Support offsets @@ -343,7 +343,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions string samplerName = GetSamplerName(context.Properties, texOp); string textureName = $"tex_{samplerName}"; string texCall = textureName + "."; - texCall += $"get_num_samples()"; + texCall += "get_num_samples()"; return texCall; } @@ -358,7 +358,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions if (texOp.Index == 3) { - texCall += $"get_num_mip_levels()"; + texCall += "get_num_mip_levels()"; } else { @@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCall += $"{lodExpr}"; } - texCall += $")"; + texCall += ")"; } return texCall; -- 2.47.1 From 02f1e289e22abd3370c3956dfb62dbfe9caef5d6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 16:38:11 +0100 Subject: [PATCH 210/368] Rebase Changes --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 6 ++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 10 ++++++++++ .../CodeGen/Msl/Instructions/InstGenMemory.cs | 4 ++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 84333e1f9..0a4123621 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -182,6 +182,12 @@ namespace Ryujinx.Graphics.Metal supportsViewportSwizzle: false, supportsIndirectParameters: true, supportsDepthClipControl: false, + uniformBufferSetIndex: 0, + storageBufferSetIndex: 1, + textureSetIndex: 2, + imageSetIndex: 3, + extraSetBaseIndex: 0, + maximumExtraSets: 0, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage, diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 4d36893d4..c00c1d09b 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -414,6 +414,11 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } + public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) + { + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + } + public void SetLineParameters(float width, bool smooth) { // Metal does not support wide-lines. @@ -517,6 +522,11 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } + public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) + { + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + } + public void SetUserClipDistance(int index, bool enableClip) { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 5a8152db7..2d38852df 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -305,7 +305,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions private static string GetSamplerName(ShaderProperties resourceDefinitions, AstTextureOperation textOp) { - return resourceDefinitions.Textures[textOp.Binding].Name; + return resourceDefinitions.Textures[textOp.GetTextureSetAndBinding()].Name; } private static string GetMaskMultiDest(int mask) @@ -362,7 +362,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } else { - context.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition); + context.Properties.Textures.TryGetValue(texOp.GetTextureSetAndBinding(), out TextureDefinition definition); bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; texCall += "get_"; -- 2.47.1 From e9aee16a279e0a8f2c5f220b54e82c5553c0d9f2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 17:14:01 +0100 Subject: [PATCH 211/368] Fix LOD sample typo --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 2d38852df..3bdc1d560 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions if (hasLodLevel) { - Append("level({Src(coordType)})"); + Append($"level({Src(coordType)})"); } // TODO: Support offsets -- 2.47.1 From db24e0f6febc7afdec37e03c340d7b3cbe3257fb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 18:31:10 +0100 Subject: [PATCH 212/368] Implement IoVariable.FrontFacing --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 1 + src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 0e98995f8..aa641476e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -180,6 +180,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { // TODO: check if it's needed context.AppendLine("float4 position [[position]];"); + context.AppendLine("bool front_facing [[front_facing]];"); } foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index b306e8283..6c11b70dc 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), IoVariable.FragmentOutputColor => ($"out.color{location}", AggregateType.Vector4 | AggregateType.FP32), IoVariable.FragmentOutputDepth => ("out.depth", AggregateType.FP32), - IoVariable.FrontFacing => ("front_facing", AggregateType.Bool), + IoVariable.FrontFacing => ("in.front_facing", AggregateType.Bool), IoVariable.GlobalId => ("thread_position_in_grid", AggregateType.Vector3 | AggregateType.U32), IoVariable.InstanceId => ("instance_id", AggregateType.S32), IoVariable.InvocationId => ("INVOCATION_ID", AggregateType.S32), -- 2.47.1 From fe4fa6f4db906f025f23ded7fdb039d6e4702363 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 23:10:00 +0100 Subject: [PATCH 213/368] Cleanup --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 5 --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 37 +++++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 0a4123621..56b554134 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -54,11 +54,6 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint) - { - return CreateBuffer(size, access); - } - public BufferHandle CreateBuffer(IntPtr pointer, int size) { var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index c00c1d09b..9d6aab7a6 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -205,26 +205,29 @@ namespace Ryujinx.Graphics.Metal public void Barrier() { - - if (_currentEncoderType == EncoderType.Render) + switch (_currentEncoderType) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + case EncoderType.Render: + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); - var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; - MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment; - renderCommandEncoder.MemoryBarrier(scope, stages, stages); - } - else if (_currentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = GetOrCreateComputeEncoder(); + var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; + MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment; + renderCommandEncoder.MemoryBarrier(scope, stages, stages); + break; + } + case EncoderType.Compute: + { + var computeCommandEncoder = GetOrCreateComputeEncoder(); - // TODO: Should there be a barrier on render targets? - var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures; - computeCommandEncoder.MemoryBarrier(scope); - } - else - { - Logger.Warning?.Print(LogClass.Gpu, "Barrier called outside of a render or compute pass"); + // TODO: Should there be a barrier on render targets? + var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures; + computeCommandEncoder.MemoryBarrier(scope); + break; + } + default: + Logger.Warning?.Print(LogClass.Gpu, "Barrier called outside of a render or compute pass"); + break; } } -- 2.47.1 From 746c8972066ac8817a89b9df48de7a6d9202a627 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 23:52:29 +0100 Subject: [PATCH 214/368] Handle Array Format SetData --- src/Ryujinx.Graphics.Metal/Texture.cs | 32 ++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 6692d6133..eb13145bb 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -201,7 +201,6 @@ namespace Ryujinx.Graphics.Metal } } - // TODO: Handle array formats public unsafe void SetData(IMemoryOwner data) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); @@ -215,14 +214,14 @@ namespace Ryujinx.Graphics.Metal int height = Info.Height; int depth = Info.Depth; int levels = Info.GetLevelsClamped(); + int layers = Info.GetLayers(); bool is3D = Info.Target == Target.Texture3D; int offset = 0; for (int level = 0; level < levels; level++) { - int mipSize = Info.GetMipSize(level); - + int mipSize = Info.GetMipSize2D(level); int endOffset = offset + mipSize; if ((uint)endOffset > (uint)dataSpan.Length) @@ -230,19 +229,22 @@ namespace Ryujinx.Graphics.Metal return; } - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - (ulong)offset, - (ulong)Info.GetMipStride(level), - (ulong)mipSize, - new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, - _mtlTexture, - 0, - (ulong)level, - new MTLOrigin() - ); + for (int layer = 0; layer < layers; layer++) + { + blitCommandEncoder.CopyFromBuffer( + mtlBuffer, + (ulong)offset, + (ulong)Info.GetMipStride(level), + (ulong)mipSize, + new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, + _mtlTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin() + ); - offset += mipSize; + offset += mipSize; + } width = Math.Max(1, width >> 1); height = Math.Max(1, height >> 1); -- 2.47.1 From a07975afec2ab7a440925bf685ecd91bf9491f0d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 02:14:56 +0100 Subject: [PATCH 215/368] Fix Pack and UnpackHalf2x16 --- .../CodeGen/Msl/Instructions/InstGen.cs | 4 +++ .../CodeGen/Msl/Instructions/InstGenHelper.cs | 4 +-- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 25 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index d35b5c9f7..8c101ad75 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -145,6 +145,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return TextureQuerySamples(context, operation); case Instruction.TextureQuerySize: return TextureQuerySize(context, operation); + case Instruction.PackHalf2x16: + return PackHalf2x16(context, operation); + case Instruction.UnpackHalf2x16: + return UnpackHalf2x16(context, operation); case Instruction.VectorExtract: return VectorExtract(context, operation); case Instruction.VoteAllEqual: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index af6e8058a..8839a1fb5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.LoopBreak, InstType.OpNullary, "break"); Add(Instruction.LoopContinue, InstType.OpNullary, "continue"); Add(Instruction.PackDouble2x32, 0); // MSL does not have a 64-bit FP - Add(Instruction.PackHalf2x16, InstType.CallUnary, "pack_half_to_unorm2x16"); + Add(Instruction.PackHalf2x16, InstType.Special); Add(Instruction.Maximum, InstType.CallBinary, "max"); Add(Instruction.MaximumU32, InstType.CallBinary, "max"); Add(Instruction.MemoryBarrier, InstType.Special); @@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.TextureQuerySize, InstType.Special); Add(Instruction.Truncate, InstType.CallUnary, "trunc"); Add(Instruction.UnpackDouble2x32, 0); // MSL does not have a 64-bit FP - Add(Instruction.UnpackHalf2x16, InstType.CallUnary, "unpack_unorm2x16_to_half"); + Add(Instruction.UnpackHalf2x16, InstType.Special); Add(Instruction.VectorExtract, InstType.Special); Add(Instruction.VoteAll, InstType.CallUnary, "simd_all"); Add(Instruction.VoteAllEqual, InstType.Special); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 3bdc1d560..daf01db04 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -394,5 +394,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return texCall; } + + public static string PackHalf2x16(CodeGenContext context, AstOperation operation) + { + IAstNode src0 = operation.GetSource(0); + IAstNode src1 = operation.GetSource(1); + + string src0Expr = GetSourceExpr(context, src0, GetSrcVarType(operation.Inst, 0)); + string src1Expr = GetSourceExpr(context, src1, GetSrcVarType(operation.Inst, 1)); + + return $"as_type(half2({src0Expr}, {src1Expr}))"; + } + + public static string UnpackHalf2x16(CodeGenContext context, AstOperation operation) + { + IAstNode src = operation.GetSource(0); + + string srcExpr = GetSourceExpr(context, src, GetSrcVarType(operation.Inst, 0)); + + return $"float2(as_type({srcExpr})){GetMask(operation.Index)}"; + } + + private static string GetMask(int index) + { + return $".{"xy".AsSpan(index, 1)}"; + } } } -- 2.47.1 From 4ee4e0935830978518fd1d0ee6e06a43d2024034 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 02:23:37 +0100 Subject: [PATCH 216/368] Fix sample-less reads with lod --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index daf01db04..652b13ee9 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -293,7 +293,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions if (hasLodLevel) { - Append($"level({Src(coordType)})"); + if (intCoords) + { + Append(Src(coordType)); + } + else + { + Append($"level({Src(coordType)})"); + } } // TODO: Support offsets -- 2.47.1 From 9ab2cd94c1493d4d15e368601d2c0205318d6b6a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 13:20:16 +0100 Subject: [PATCH 217/368] Fix Clear Viewport --- .../EncoderStateManager.cs | 2 ++ src/Ryujinx.Graphics.Metal/HelperShader.cs | 36 +++++++++++++++++-- src/Ryujinx.Graphics.Metal/Pipeline.cs | 7 ++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index eef8fd341..0ca7dbd2d 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -25,6 +25,8 @@ namespace Ryujinx.Graphics.Metal public readonly MTLIndexType IndexType => _currentState.IndexType; public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; public readonly PrimitiveTopology Topology => _currentState.Topology; + public readonly Texture RenderTarget => _currentState.RenderTargets[0]; + public readonly Texture DepthStencil => _currentState.DepthStencil; // RGBA32F is the biggest format private const int ZeroBufferSize = 4 * 4; diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 5adc336f0..5066b0244 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -202,19 +202,35 @@ namespace Ryujinx.Graphics.Metal public unsafe void ClearColor( int index, - ReadOnlySpan clearColor) + ReadOnlySpan clearColor, + uint componentMask, + int dstWidth, + int dstHeight) { const int ClearColorBufferSize = 16; // Save current state _pipeline.SaveState(); + Span viewports = stackalloc Viewport[1]; + + // TODO: Set exact viewport! + viewports[0] = new Viewport( + new Rectangle(0, 0, dstWidth, dstHeight), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + _pipeline.SetProgram(_programsColorClear[index]); _pipeline.SetBlendState(index, new BlendDescriptor(false, new ColorF(0f, 0f, 0f, 1f), BlendOp.Add, BlendFactor.One, BlendFactor.Zero, BlendOp.Add, BlendFactor.One, BlendFactor.Zero)); _pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - // _pipeline.SetRenderTargetColorMasks([componentMask]); + _pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + _pipeline.SetViewports(viewports); fixed (float* ptr = clearColor) { @@ -231,7 +247,9 @@ namespace Ryujinx.Graphics.Metal float depthValue, bool depthMask, int stencilValue, - int stencilMask) + int stencilMask, + int dstWidth, + int dstHeight) { const int ClearDepthBufferSize = 4; @@ -240,10 +258,22 @@ namespace Ryujinx.Graphics.Metal // Save current state _pipeline.SaveState(); + Span viewports = stackalloc Viewport[1]; + + viewports[0] = new Viewport( + new Rectangle(0, 0, dstWidth, dstHeight), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + _pipeline.SetProgram(_programDepthStencilClear); _pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + _pipeline.SetViewports(viewports); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); // _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); _pipeline.GetOrCreateRenderEncoder().SetFragmentBytes(ptr, ClearDepthBufferSize, 0); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 9d6aab7a6..912a28ac0 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -250,13 +250,16 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; + var dst = _encoderStateManager.RenderTarget; - _helperShader.ClearColor(index, colors); + _helperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - _helperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask); + var depthStencil = _encoderStateManager.DepthStencil; + + _helperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height); } public void CommandBufferBarrier() -- 2.47.1 From 24ab7788d80781832923903da3a687e05a2e7f4b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 13:20:37 +0100 Subject: [PATCH 218/368] Fix 3D -> 3D Texture Copies --- src/Ryujinx.Graphics.Metal/Texture.cs | 68 ++++++++++++++++++++------- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index eb13145bb..4ec5773bf 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -98,15 +98,31 @@ namespace Ryujinx.Graphics.Metal if (destination is Texture destinationTexture) { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)firstLayer, - (ulong)firstLevel, - destinationTexture._mtlTexture, - (ulong)firstLayer, - (ulong)firstLevel, - _mtlTexture.ArrayLength, - _mtlTexture.MipmapLevelCount); + if (destinationTexture.Info.Target == Target.Texture3D) + { + blitCommandEncoder.CopyFromTexture( + _mtlTexture, + 0, + (ulong)firstLevel, + new MTLOrigin { x = 0, y = 0, z = (ulong)firstLayer }, + new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1}, + destinationTexture._mtlTexture, + 0, + (ulong)firstLevel, + new MTLOrigin { x = 0, y = 0, z = (ulong)firstLayer }); + } + else + { + blitCommandEncoder.CopyFromTexture( + _mtlTexture, + (ulong)firstLayer, + (ulong)firstLevel, + destinationTexture._mtlTexture, + (ulong)firstLayer, + (ulong)firstLevel, + _mtlTexture.ArrayLength, + _mtlTexture.MipmapLevelCount); + } } } @@ -116,15 +132,31 @@ namespace Ryujinx.Graphics.Metal if (destination is Texture destinationTexture) { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)srcLayer, - (ulong)srcLevel, - destinationTexture._mtlTexture, - (ulong)dstLayer, - (ulong)dstLevel, - _mtlTexture.ArrayLength, - _mtlTexture.MipmapLevelCount); + if (destinationTexture.Info.Target == Target.Texture3D) + { + blitCommandEncoder.CopyFromTexture( + _mtlTexture, + 0, + (ulong)srcLevel, + new MTLOrigin { x = 0, y = 0, z = (ulong)srcLayer }, + new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1}, + destinationTexture._mtlTexture, + 0, + (ulong)dstLevel, + new MTLOrigin { x = 0, y = 0, z = (ulong)dstLayer }); + } + else + { + blitCommandEncoder.CopyFromTexture( + _mtlTexture, + (ulong)srcLayer, + (ulong)srcLevel, + destinationTexture._mtlTexture, + (ulong)dstLayer, + (ulong)dstLevel, + _mtlTexture.ArrayLength, + _mtlTexture.MipmapLevelCount); + } } } -- 2.47.1 From c883ebb645c8844d813fe85cb59c555fd0e49797 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 13:24:05 +0100 Subject: [PATCH 219/368] Workaround for Wonder --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 912a28ac0..7cdc2c6d8 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -252,6 +252,13 @@ namespace Ryujinx.Graphics.Metal float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; var dst = _encoderStateManager.RenderTarget; + // TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format + if (dst == null) + { + Logger.Warning?.PrintMsg(LogClass.Gpu, "Attempted to clear invalid render target!"); + return; + } + _helperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height); } @@ -259,6 +266,13 @@ namespace Ryujinx.Graphics.Metal { var depthStencil = _encoderStateManager.DepthStencil; + // TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format + if (depthStencil == null) + { + Logger.Warning?.PrintMsg(LogClass.Gpu, "Attempted to clear invalid depth stencil!"); + return; + } + _helperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height); } -- 2.47.1 From e615d7d849e15abff4dbc59102a7c4773b34f1e6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 16:11:48 +0100 Subject: [PATCH 220/368] Fix modulo operator Support sample offsets Include FragmentIn as additional arg Always declare frag output struct SubgroupLaneId --- .../CodeGen/Msl/CodeGenContext.cs | 2 +- .../CodeGen/Msl/Declarations.cs | 6 +-- .../CodeGen/Msl/Instructions/InstGenCall.cs | 7 +-- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 2 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 49 ++++++++++++++++++- .../CodeGen/Msl/Instructions/IoMap.cs | 1 + .../CodeGen/Msl/MslGenerator.cs | 6 ++- 7 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index 0b0d598c5..79c13964c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string Tab = " "; // The number of additional arguments that every function (except for the main one) must have (for instance support_buffer) - public const int additionalArgCount = 1; + public const int AdditionalArgCount = 2; public StructuredFunction CurrentFunction { get; set; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index aa641476e..8d4a9c877 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } - private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable inputs) + private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable outputs) { if (context.Definitions.IaIndexing) { @@ -228,7 +228,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else { - if (inputs.Any()) + if (outputs.Any() || context.Definitions.Stage == ShaderStage.Fragment) { string prefix = ""; @@ -247,7 +247,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.EnterScope(); - foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) + foreach (var ioDefinition in outputs.OrderBy(x => x.Location)) { string type = ioDefinition.IoVariable switch { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs index f233908c4..5df3aa282 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs @@ -13,12 +13,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions var functon = context.GetFunction(funcId.Value); int argCount = operation.SourcesCount - 1; - string[] args = new string[argCount + CodeGenContext.additionalArgCount]; + string[] args = new string[argCount + CodeGenContext.AdditionalArgCount]; // Additional arguments - args[0] = "support_buffer"; + args[0] = "in"; + args[1] = "support_buffer"; - int argIndex = CodeGenContext.additionalArgCount; + int argIndex = CodeGenContext.AdditionalArgCount; for (int i = 0; i < argCount; i++) { args[argIndex++] = GetSourceExpr(context, operation.GetSource(i + 1), functon.GetArgumentType(i)); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 8839a1fb5..406fda11a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.MemoryBarrier, InstType.Special); Add(Instruction.Minimum, InstType.CallBinary, "min"); Add(Instruction.MinimumU32, InstType.CallBinary, "min"); - Add(Instruction.Modulo, InstType.CallBinary, "%"); + Add(Instruction.Modulo, InstType.CallBinary, "fmod"); Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1); Add(Instruction.MultiplyHighS32, InstType.CallBinary, "mulhi"); Add(Instruction.MultiplyHighU32, InstType.CallBinary, "mulhi"); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 652b13ee9..28d8007cd 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; @@ -193,11 +194,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions AstTextureOperation texOp = (AstTextureOperation)operation; bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; - bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; + bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0; bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0; bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0; + bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; + bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; bool colorIsVector = isGather || !isShadow; @@ -291,6 +296,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Append(Src(AggregateType.S32)); } + if (hasDerivatives) + { + Logger.Warning?.PrintMsg(LogClass.Gpu, "Unused sampler derivatives!"); + } + + if (hasLodBias) + { + Logger.Warning?.PrintMsg(LogClass.Gpu, "Unused sample LOD bias!"); + } + if (hasLodLevel) { if (intCoords) @@ -303,7 +318,37 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } } - // TODO: Support offsets + string AssembleOffsetVector(int count) + { + if (count > 1) + { + string[] elems = new string[count]; + + for (int index = 0; index < count; index++) + { + elems[index] = Src(AggregateType.S32); + } + + return "int" + count + "(" + string.Join(", ", elems) + ")"; + } + else + { + return Src(AggregateType.S32); + } + } + + // TODO: Support reads with offsets + if (!intCoords) + { + if (hasOffset) + { + Append(AssembleOffsetVector(coordsCount)); + } + else if (hasOffsets) + { + Logger.Warning?.PrintMsg(LogClass.Gpu, "Multiple offsets on gathers are not yet supported!"); + } + } texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 6c11b70dc..2e93310aa 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -33,6 +33,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), IoVariable.ThreadId => ("thread_position_in_threadgroup", AggregateType.Vector3 | AggregateType.U32), + IoVariable.SubgroupLaneId => ("thread_index_in_simdgroup", AggregateType.U32), IoVariable.VertexId => ("vertex_id", AggregateType.S32), // gl_VertexIndex does not have a direct equivalent in MSL IoVariable.VertexIndex => ("vertex_id", AggregateType.U32), diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 2866574eb..87512a961 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -63,14 +63,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl ShaderStage stage, bool isMainFunc = false) { - int additionalArgCount = isMainFunc ? 0 : CodeGenContext.additionalArgCount; + int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount; string[] args = new string[additionalArgCount + function.InArguments.Length + function.OutArguments.Length]; // All non-main functions need to be able to access the support_buffer as well if (!isMainFunc) { - args[0] = "constant Struct_support_buffer* support_buffer"; + args[0] = "FragmentIn in"; + args[1] = "constant Struct_support_buffer* support_buffer"; } int argIndex = additionalArgCount; @@ -135,6 +136,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl args = args.Append("uint3 threadgroup_position_in_grid [[threadgroup_position_in_grid]]").ToArray(); args = args.Append("uint3 thread_position_in_grid [[thread_position_in_grid]]").ToArray(); args = args.Append("uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]]").ToArray(); + args = args.Append("uint thread_index_in_simdgroup [[thread_index_in_simdgroup]]").ToArray(); } foreach (var constantBuffer in context.Properties.ConstantBuffers.Values) -- 2.47.1 From 39bfd55958aecaab965a24d25dc0b446a07cbc3c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 21:26:20 +0100 Subject: [PATCH 221/368] Disable Vector Indexing Bug Workaround --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 56b554134..06ce6e5d5 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -136,7 +136,7 @@ namespace Ryujinx.Graphics.Metal vendorName: HardwareInfoTools.GetVendor(), SystemMemoryType.UnifiedMemory, hasFrontFacingBug: false, - hasVectorIndexingBug: true, + hasVectorIndexingBug: false, needsFragmentOutputSpecialization: true, reduceShaderPrecision: true, supportsAstcCompression: true, -- 2.47.1 From 51b4ffeb6c68e60361ee877fe5629163516e3295 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 21:46:16 +0100 Subject: [PATCH 222/368] Disable scaled vertex formats --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 06ce6e5d5..f5a1f8e97 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Metal supportsBgraFormat: true, supportsR4G4Format: false, supportsR4G4B4A4Format: true, - supportsScaledVertexFormats: true, + supportsScaledVertexFormats: false, supportsSnormBufferTextureFormat: true, supportsSparseBuffer: false, supports5BitComponentFormat: true, -- 2.47.1 From 36ac0414e21b1d8aa00b6ac2fb4732562b022669 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 31 May 2024 07:45:14 +0200 Subject: [PATCH 223/368] override Equals for render pipeline hash --- src/Ryujinx.Graphics.Metal/Constants.cs | 4 +- .../RenderPipelineCache.cs | 121 ++++++++++++++++-- 2 files changed, 111 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index a413fcce1..3f64c6ee3 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.Metal public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; public const int MaxColorAttachments = 8; // TODO: Check this value - public const int MaxVertexAttributes = 16; + public const int MaxVertexAttributes = 31; // TODO: Check this value - public const int MaxVertexLayouts = 16; + public const int MaxVertexLayouts = 31; public const int MaxTextures = 31; public const int MaxSamplers = 16; } diff --git a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs index d564ef629..6fb171816 100644 --- a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs +++ b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Metal public MTLBlendFactor DestinationRGBBlendFactor; public MTLBlendFactor SourceAlphaBlendFactor; public MTLBlendFactor DestinationAlphaBlendFactor; + public MTLColorWriteMask WriteMask; } [System.Runtime.CompilerServices.InlineArray(Constants.MaxColorAttachments)] public struct ColorAttachmentHashArray @@ -39,8 +40,8 @@ namespace Ryujinx.Graphics.Metal public struct AttributeHash { public MTLVertexFormat Format; - public int Offset; - public int BufferIndex; + public ulong Offset; + public ulong BufferIndex; } [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexAttributes)] public struct AttributeHashArray @@ -50,10 +51,9 @@ namespace Ryujinx.Graphics.Metal public AttributeHashArray Attributes; public struct LayoutHash { - public MTLVertexFormat Format; - public int Stride; - public int StepFunction; - public int StepRate; + public ulong Stride; + public MTLVertexStepFunction StepFunction; + public ulong StepRate; } [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexLayouts)] public struct LayoutHashArray @@ -63,6 +63,102 @@ namespace Ryujinx.Graphics.Metal public LayoutHashArray Layouts; } public VertexDescriptorHash VertexDescriptor; + + public override bool Equals(object obj) + { + if (obj is not RenderPipelineHash other) + { + return false; + } + + if (VertexFunction != other.VertexFunction) + { + return false; + } + if (FragmentFunction != other.FragmentFunction) + { + return false; + } + if (DepthStencilAttachment.DepthPixelFormat != other.DepthStencilAttachment.DepthPixelFormat) + { + return false; + } + if (DepthStencilAttachment.StencilPixelFormat != other.DepthStencilAttachment.StencilPixelFormat) + { + return false; + } + for (int i = 0; i < Constants.MaxColorAttachments; i++) + { + if (ColorAttachments[i].PixelFormat != other.ColorAttachments[i].PixelFormat) + { + return false; + } + if (ColorAttachments[i].BlendingEnabled != other.ColorAttachments[i].BlendingEnabled) + { + return false; + } + if (ColorAttachments[i].RgbBlendOperation != other.ColorAttachments[i].RgbBlendOperation) + { + return false; + } + if (ColorAttachments[i].AlphaBlendOperation != other.ColorAttachments[i].AlphaBlendOperation) + { + return false; + } + if (ColorAttachments[i].SourceRGBBlendFactor != other.ColorAttachments[i].SourceRGBBlendFactor) + { + return false; + } + if (ColorAttachments[i].DestinationRGBBlendFactor != other.ColorAttachments[i].DestinationRGBBlendFactor) + { + return false; + } + if (ColorAttachments[i].SourceAlphaBlendFactor != other.ColorAttachments[i].SourceAlphaBlendFactor) + { + return false; + } + if (ColorAttachments[i].DestinationAlphaBlendFactor != other.ColorAttachments[i].DestinationAlphaBlendFactor) + { + return false; + } + if (ColorAttachments[i].WriteMask != other.ColorAttachments[i].WriteMask) + { + return false; + } + } + for (int i = 0; i < Constants.MaxVertexAttributes; i++) + { + if (VertexDescriptor.Attributes[i].Format != other.VertexDescriptor.Attributes[i].Format) + { + return false; + } + if (VertexDescriptor.Attributes[i].Offset != other.VertexDescriptor.Attributes[i].Offset) + { + return false; + } + if (VertexDescriptor.Attributes[i].BufferIndex != other.VertexDescriptor.Attributes[i].BufferIndex) + { + return false; + } + } + for (int i = 0; i < Constants.MaxVertexLayouts; i++) + { + if (VertexDescriptor.Layouts[i].Stride != other.VertexDescriptor.Layouts[i].Stride) + { + return false; + } + if (VertexDescriptor.Layouts[i].StepFunction != other.VertexDescriptor.Layouts[i].StepFunction) + { + return false; + } + if (VertexDescriptor.Layouts[i].StepRate != other.VertexDescriptor.Layouts[i].StepRate) + { + return false; + } + } + + return true; + } } [SupportedOSPlatform("macos")] @@ -102,7 +198,8 @@ namespace Ryujinx.Graphics.Metal SourceRGBBlendFactor = attachment.SourceRGBBlendFactor, DestinationRGBBlendFactor = attachment.DestinationRGBBlendFactor, SourceAlphaBlendFactor = attachment.SourceAlphaBlendFactor, - DestinationAlphaBlendFactor = attachment.DestinationAlphaBlendFactor + DestinationAlphaBlendFactor = attachment.DestinationAlphaBlendFactor, + WriteMask = attachment.WriteMask }; } @@ -116,8 +213,8 @@ namespace Ryujinx.Graphics.Metal hash.VertexDescriptor.Attributes[i] = new RenderPipelineHash.VertexDescriptorHash.AttributeHash { Format = attribute.Format, - Offset = (int)attribute.Offset, - BufferIndex = (int)attribute.BufferIndex + Offset = attribute.Offset, + BufferIndex = attribute.BufferIndex }; } @@ -127,9 +224,9 @@ namespace Ryujinx.Graphics.Metal var layout = descriptor.VertexDescriptor.Layouts.Object((ulong)i); hash.VertexDescriptor.Layouts[i] = new RenderPipelineHash.VertexDescriptorHash.LayoutHash { - Stride = (int)layout.Stride, - StepFunction = (int)layout.StepFunction, - StepRate = (int)layout.StepRate + Stride = layout.Stride, + StepFunction = layout.StepFunction, + StepRate = layout.StepRate }; } -- 2.47.1 From 65bddcd475ce841237ddbc4512723c36708ce321 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 11:24:33 +0100 Subject: [PATCH 224/368] Map R5G5B5A1Unorm --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 7cac4f7c4..e3f531cdd 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Metal // Add(Format.R4G4Unorm, MTLPixelFormat.R4G4Unorm); // Add(Format.R4G4B4A4Unorm, MTLPixelFormat.R4G4B4A4Unorm); // Add(Format.R5G5B5X1Unorm, MTLPixelFormat.R5G5B5X1Unorm); - // Add(Format.R5G5B5A1Unorm, MTLPixelFormat.R5G5B5A1Unorm); + Add(Format.R5G5B5A1Unorm, MTLPixelFormat.BGR5A1Unorm); Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm); Add(Format.R10G10B10A2Unorm, MTLPixelFormat.RGB10A2Unorm); Add(Format.R10G10B10A2Uint, MTLPixelFormat.RGB10A2Uint); -- 2.47.1 From 5423ad9ae1f1f3c71c7e204ee70d888ed9832277 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 11:43:26 +0100 Subject: [PATCH 225/368] Depth Bias --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 ++++ .../EncoderStateManager.cs | 22 +++++++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index a75ea3dc9..deee78a60 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -51,6 +51,10 @@ namespace Ryujinx.Graphics.Metal public MTLCompareFunction DepthCompareFunction = MTLCompareFunction.Always; public bool DepthWriteEnabled = false; + public float DepthBias; + public float SlopeScale; + public float Clamp; + public MTLStencilDescriptor BackFaceStencil = new(); public MTLStencilDescriptor FrontFaceStencil = new(); public int BackRefValue = 0; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 0ca7dbd2d..74bdc4258 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -79,6 +79,7 @@ namespace Ryujinx.Graphics.Metal // Set all the inline state, since it might have changed var renderCommandEncoder = _pipeline.GetOrCreateRenderEncoder(); SetDepthClamp(renderCommandEncoder); + SetDepthBias(renderCommandEncoder); SetScissors(renderCommandEncoder); SetViewports(renderCommandEncoder); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); @@ -165,6 +166,7 @@ namespace Ryujinx.Graphics.Metal // Rebind all the state SetDepthClamp(renderCommandEncoder); + SetDepthBias(renderCommandEncoder); SetCullMode(renderCommandEncoder); SetFrontFace(renderCommandEncoder); SetStencilRefValue(renderCommandEncoder); @@ -561,6 +563,21 @@ namespace Ryujinx.Graphics.Metal } } + // Inlineable + public void UpdateDepthBias(float depthBias, float slopeScale, float clamp) + { + _currentState.DepthBias = depthBias; + _currentState.SlopeScale = slopeScale; + _currentState.Clamp = clamp; + + // Inline update + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetDepthBias(renderCommandEncoder); + } + } + // Inlineable public void UpdateScissors(ReadOnlySpan> regions) { @@ -839,6 +856,11 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetDepthClipMode(_currentState.DepthClipMode); } + private readonly void SetDepthBias(MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetDepthBias(_currentState.DepthBias, _currentState.SlopeScale, _currentState.Clamp); + } + private unsafe void SetScissors(MTLRenderCommandEncoder renderCommandEncoder) { if (_currentState.Scissors.Length > 0) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 7cdc2c6d8..34c4d30cb 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -388,7 +388,7 @@ namespace Ryujinx.Graphics.Metal public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _encoderStateManager.UpdateDepthBias(units, factor, clamp); } public void SetDepthClamp(bool clamp) -- 2.47.1 From 9e61294671cb2f319cf46a91105fc120b93b7e82 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 13:20:39 +0100 Subject: [PATCH 226/368] Fix sample compare --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 28d8007cd..135cd80e0 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions if (isShadow) { - Append(Src(AggregateType.S32)); + Append(Src(AggregateType.FP32)); } if (hasDerivatives) -- 2.47.1 From 8b5392ce9b359b17057729ce60ba3bd363cb3178 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 14:01:32 +0100 Subject: [PATCH 227/368] =?UTF-8?q?Don=E2=80=99t=20use=20DidModifyRange?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index f5a1f8e97..f9ed0423b 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -220,11 +220,6 @@ namespace Ryujinx.Graphics.Metal { var span = new Span(src.Contents.ToPointer(), data.Length); data.CopyTo(span); - src.DidModifyRange(new NSRange - { - location = 0, - length = (ulong)data.Length - }); MTLBuffer dst = new(Unsafe.As(ref buffer)); blitEncoder.CopyFromBuffer(src, 0, dst, (ulong)offset, (ulong)data.Length); -- 2.47.1 From e49702965ff73c7d9cf688d9ae0d3fde00ef0e31 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 14:02:22 +0100 Subject: [PATCH 228/368] Print shader code involved in failed linking --- src/Ryujinx.Graphics.Metal/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 8ff690463..89f0bd5dd 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.Metal var shaderLibrary = device.NewLibrary(StringHelper.NSString(shader.Code), new MTLCompileOptions(IntPtr.Zero), ref libraryError); if (libraryError != IntPtr.Zero) { + Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code); Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); _status = ProgramLinkStatus.Failure; return; -- 2.47.1 From 585bdc2b549de319116979f0c0e1e519de30de52 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 14:03:38 +0100 Subject: [PATCH 229/368] Log failed format conversions --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index e3f531cdd..529758e78 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -180,6 +180,11 @@ namespace Ryujinx.Graphics.Metal } } + if (mtlFormat == MTLPixelFormat.Invalid) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Application requested {format}, no direct equivalent was found!"); + } + return mtlFormat; } } -- 2.47.1 From dda746c0fbf59fc33af6b2bfd21537c9689c349b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Wed, 19 Jun 2024 23:13:55 +0100 Subject: [PATCH 230/368] Metal: Buffers Take 2 (#21) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Basic BufferManager * Start Scoped Command Buffers * Fences stuff * Remember to cleanup sync manager * Auto, Command Buffer Dependants * Cleanup * Cleanup + Fix Texture->Buffer Copies * Slow buffer upload * Cleanup + Rework TextureBuffer * Don’t get unsafe * Cleanup * Goddamn it * Staging Buffer + Interrupt Action + Flush --- src/Ryujinx.Graphics.Metal/Auto.cs | 146 +++++++++ src/Ryujinx.Graphics.Metal/BitMap.cs | 157 ++++++++++ src/Ryujinx.Graphics.Metal/BufferHolder.cs | 285 +++++++++++++++++ src/Ryujinx.Graphics.Metal/BufferInfo.cs | 11 - src/Ryujinx.Graphics.Metal/BufferManager.cs | 203 ++++++++++++ .../BufferUsageBitmap.cs | 85 +++++ .../CommandBufferPool.cs | 275 ++++++++++++++++ .../CommandBufferScoped.cs | 41 +++ src/Ryujinx.Graphics.Metal/Constants.cs | 2 + .../DisposableBuffer.cs | 26 ++ src/Ryujinx.Graphics.Metal/EncoderState.cs | 7 +- .../EncoderStateManager.cs | 95 +++--- src/Ryujinx.Graphics.Metal/FenceHolder.cs | 77 +++++ src/Ryujinx.Graphics.Metal/Handle.cs | 14 - src/Ryujinx.Graphics.Metal/HelperShader.cs | 2 +- src/Ryujinx.Graphics.Metal/IdList.cs | 121 +++++++ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 88 +++--- .../MultiFenceHolder.cs | 262 ++++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 86 +++-- src/Ryujinx.Graphics.Metal/StagingBuffer.cs | 294 ++++++++++++++++++ src/Ryujinx.Graphics.Metal/SyncManager.cs | 214 +++++++++++++ src/Ryujinx.Graphics.Metal/Texture.cs | 17 +- src/Ryujinx.Graphics.Metal/TextureBase.cs | 6 +- src/Ryujinx.Graphics.Metal/TextureBuffer.cs | 71 ++--- 24 files changed, 2380 insertions(+), 205 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Auto.cs create mode 100644 src/Ryujinx.Graphics.Metal/BitMap.cs create mode 100644 src/Ryujinx.Graphics.Metal/BufferHolder.cs delete mode 100644 src/Ryujinx.Graphics.Metal/BufferInfo.cs create mode 100644 src/Ryujinx.Graphics.Metal/BufferManager.cs create mode 100644 src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs create mode 100644 src/Ryujinx.Graphics.Metal/CommandBufferPool.cs create mode 100644 src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs create mode 100644 src/Ryujinx.Graphics.Metal/DisposableBuffer.cs create mode 100644 src/Ryujinx.Graphics.Metal/FenceHolder.cs delete mode 100644 src/Ryujinx.Graphics.Metal/Handle.cs create mode 100644 src/Ryujinx.Graphics.Metal/IdList.cs create mode 100644 src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs create mode 100644 src/Ryujinx.Graphics.Metal/StagingBuffer.cs create mode 100644 src/Ryujinx.Graphics.Metal/SyncManager.cs diff --git a/src/Ryujinx.Graphics.Metal/Auto.cs b/src/Ryujinx.Graphics.Metal/Auto.cs new file mode 100644 index 000000000..8793923a7 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Auto.cs @@ -0,0 +1,146 @@ +using System; +using System.Diagnostics; +using System.Runtime.Versioning; +using System.Threading; + +namespace Ryujinx.Graphics.Metal +{ + public interface IAuto + { + bool HasCommandBufferDependency(CommandBufferScoped cbs); + + void IncrementReferenceCount(); + void DecrementReferenceCount(int cbIndex); + void DecrementReferenceCount(); + } + + public interface IAutoPrivate : IAuto + { + void AddCommandBufferDependencies(CommandBufferScoped cbs); + } + + [SupportedOSPlatform("macos")] + public class Auto : IAutoPrivate, IDisposable where T : IDisposable + { + private int _referenceCount; + private T _value; + + private readonly BitMap _cbOwnership; + private readonly MultiFenceHolder _waitable; + + private bool _disposed; + private bool _destroyed; + + public Auto(T value) + { + _referenceCount = 1; + _value = value; + _cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers); + } + + public Auto(T value, MultiFenceHolder waitable) : this(value) + { + _waitable = waitable; + } + + public T Get(CommandBufferScoped cbs, int offset, int size, bool write = false) + { + _waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, write); + return Get(cbs); + } + + public T GetUnsafe() + { + return _value; + } + + public T Get(CommandBufferScoped cbs) + { + if (!_destroyed) + { + AddCommandBufferDependencies(cbs); + } + + return _value; + } + + public bool HasCommandBufferDependency(CommandBufferScoped cbs) + { + return _cbOwnership.IsSet(cbs.CommandBufferIndex); + } + + public bool HasRentedCommandBufferDependency(CommandBufferPool cbp) + { + return _cbOwnership.AnySet(); + } + + public void AddCommandBufferDependencies(CommandBufferScoped cbs) + { + // We don't want to add a reference to this object to the command buffer + // more than once, so if we detect that the command buffer already has ownership + // of this object, then we can just return without doing anything else. + if (_cbOwnership.Set(cbs.CommandBufferIndex)) + { + if (_waitable != null) + { + cbs.AddWaitable(_waitable); + } + + cbs.AddDependant(this); + } + } + + public bool TryIncrementReferenceCount() + { + int lastValue; + do + { + lastValue = _referenceCount; + + if (lastValue == 0) + { + return false; + } + } + while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); + + return true; + } + + public void IncrementReferenceCount() + { + if (Interlocked.Increment(ref _referenceCount) == 1) + { + Interlocked.Decrement(ref _referenceCount); + throw new InvalidOperationException("Attempted to increment the reference count of an object that was already destroyed."); + } + } + + public void DecrementReferenceCount(int cbIndex) + { + _cbOwnership.Clear(cbIndex); + DecrementReferenceCount(); + } + + public void DecrementReferenceCount() + { + if (Interlocked.Decrement(ref _referenceCount) == 0) + { + _value.Dispose(); + _value = default; + _destroyed = true; + } + + Debug.Assert(_referenceCount >= 0); + } + + public void Dispose() + { + if (!_disposed) + { + DecrementReferenceCount(); + _disposed = true; + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/BitMap.cs b/src/Ryujinx.Graphics.Metal/BitMap.cs new file mode 100644 index 000000000..4ddc438c1 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/BitMap.cs @@ -0,0 +1,157 @@ +namespace Ryujinx.Graphics.Metal +{ + readonly struct BitMap + { + public const int IntSize = 64; + + private const int IntShift = 6; + private const int IntMask = IntSize - 1; + + private readonly long[] _masks; + + public BitMap(int count) + { + _masks = new long[(count + IntMask) / IntSize]; + } + + public bool AnySet() + { + for (int i = 0; i < _masks.Length; i++) + { + if (_masks[i] != 0) + { + return true; + } + } + + return false; + } + + public bool IsSet(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + return (_masks[wordIndex] & wordMask) != 0; + } + + public bool IsSet(int start, int end) + { + if (start == end) + { + return IsSet(start); + } + + int startIndex = start >> IntShift; + int startBit = start & IntMask; + long startMask = -1L << startBit; + + int endIndex = end >> IntShift; + int endBit = end & IntMask; + long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); + + if (startIndex == endIndex) + { + return (_masks[startIndex] & startMask & endMask) != 0; + } + + if ((_masks[startIndex] & startMask) != 0) + { + return true; + } + + for (int i = startIndex + 1; i < endIndex; i++) + { + if (_masks[i] != 0) + { + return true; + } + } + + if ((_masks[endIndex] & endMask) != 0) + { + return true; + } + + return false; + } + + public bool Set(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + if ((_masks[wordIndex] & wordMask) != 0) + { + return false; + } + + _masks[wordIndex] |= wordMask; + + return true; + } + + public void SetRange(int start, int end) + { + if (start == end) + { + Set(start); + return; + } + + int startIndex = start >> IntShift; + int startBit = start & IntMask; + long startMask = -1L << startBit; + + int endIndex = end >> IntShift; + int endBit = end & IntMask; + long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); + + if (startIndex == endIndex) + { + _masks[startIndex] |= startMask & endMask; + } + else + { + _masks[startIndex] |= startMask; + + for (int i = startIndex + 1; i < endIndex; i++) + { + _masks[i] |= -1; + } + + _masks[endIndex] |= endMask; + } + } + + public void Clear(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + _masks[wordIndex] &= ~wordMask; + } + + public void Clear() + { + for (int i = 0; i < _masks.Length; i++) + { + _masks[i] = 0; + } + } + + public void ClearInt(int start, int end) + { + for (int i = start; i <= end; i++) + { + _masks[i] = 0; + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs new file mode 100644 index 000000000..7fe3530eb --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/BufferHolder.cs @@ -0,0 +1,285 @@ +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class BufferHolder : IDisposable + { + public int Size { get; } + + private readonly IntPtr _map; + private readonly MetalRenderer _renderer; + private readonly Pipeline _pipeline; + + private readonly MultiFenceHolder _waitable; + private readonly Auto _buffer; + + private readonly ReaderWriterLockSlim _flushLock; + private FenceHolder _flushFence; + private int _flushWaiting; + + public BufferHolder(MetalRenderer renderer, Pipeline pipeline, MTLBuffer buffer, int size) + { + _renderer = renderer; + _pipeline = pipeline; + _map = buffer.Contents; + _waitable = new MultiFenceHolder(size); + _buffer = new Auto(new(buffer), _waitable); + + _flushLock = new ReaderWriterLockSlim(); + + Size = size; + } + + public Auto GetBuffer() + { + return _buffer; + } + + public Auto GetBuffer(bool isWrite) + { + if (isWrite) + { + SignalWrite(0, Size); + } + + return _buffer; + } + + public Auto GetBuffer(int offset, int size, bool isWrite) + { + if (isWrite) + { + SignalWrite(offset, size); + } + + return _buffer; + } + + public void SignalWrite(int offset, int size) + { + if (offset == 0 && size == Size) + { + // TODO: Cache converted buffers + } + else + { + // TODO: Cache converted buffers + } + } + + private void ClearFlushFence() + { + // Assumes _flushLock is held as writer. + + if (_flushFence != null) + { + if (_flushWaiting == 0) + { + _flushFence.Put(); + } + + _flushFence = null; + } + } + + private void WaitForFlushFence() + { + if (_flushFence == null) + { + return; + } + + // If storage has changed, make sure the fence has been reached so that the data is in place. + _flushLock.ExitReadLock(); + _flushLock.EnterWriteLock(); + + if (_flushFence != null) + { + var fence = _flushFence; + Interlocked.Increment(ref _flushWaiting); + + // Don't wait in the lock. + + _flushLock.ExitWriteLock(); + + fence.Wait(); + + _flushLock.EnterWriteLock(); + + if (Interlocked.Decrement(ref _flushWaiting) == 0) + { + fence.Put(); + } + + _flushFence = null; + } + + // Assumes the _flushLock is held as reader, returns in same state. + _flushLock.ExitWriteLock(); + _flushLock.EnterReadLock(); + } + + public PinnedSpan GetData(int offset, int size) + { + _flushLock.EnterReadLock(); + + WaitForFlushFence(); + + Span result; + + if (_map != IntPtr.Zero) + { + result = GetDataStorage(offset, size); + + // Need to be careful here, the buffer can't be unmapped while the data is being used. + _buffer.IncrementReferenceCount(); + + _flushLock.ExitReadLock(); + + return PinnedSpan.UnsafeFromSpan(result, _buffer.DecrementReferenceCount); + } + + throw new InvalidOperationException("The buffer is not mapped"); + } + + public unsafe Span GetDataStorage(int offset, int size) + { + int mappingSize = Math.Min(size, Size - offset); + + if (_map != IntPtr.Zero) + { + return new Span((void*)(_map + offset), mappingSize); + } + + throw new InvalidOperationException("The buffer is not mapped."); + } + + public unsafe void SetData(int offset, ReadOnlySpan data, CommandBufferScoped? cbs = null, Action endRenderPass = null, bool allowCbsWait = true) + { + int dataSize = Math.Min(data.Length, Size - offset); + if (dataSize == 0) + { + return; + } + + if (_map != IntPtr.Zero) + { + // If persistently mapped, set the data directly if the buffer is not currently in use. + bool isRented = _buffer.HasRentedCommandBufferDependency(_renderer.CommandBufferPool); + + // If the buffer is rented, take a little more time and check if the use overlaps this handle. + bool needsFlush = isRented && _waitable.IsBufferRangeInUse(offset, dataSize, false); + + if (!needsFlush) + { + WaitForFences(offset, dataSize); + + data[..dataSize].CopyTo(new Span((void*)(_map + offset), dataSize)); + + SignalWrite(offset, dataSize); + + return; + } + } + + if (allowCbsWait) + { + _renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, endRenderPass, this, offset, data); + } + else + { + bool rentCbs = cbs == null; + if (rentCbs) + { + cbs = _renderer.CommandBufferPool.Rent(); + } + + if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, endRenderPass, this, offset, data)) + { + // Need to do a slow upload. + BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize); + srcHolder.SetDataUnchecked(0, data); + + var srcBuffer = srcHolder.GetBuffer(); + var dstBuffer = this.GetBuffer(true); + + Copy(_pipeline, cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize); + + srcHolder.Dispose(); + } + + if (rentCbs) + { + cbs.Value.Dispose(); + } + } + } + + public unsafe void SetDataUnchecked(int offset, ReadOnlySpan data) + { + int dataSize = Math.Min(data.Length, Size - offset); + if (dataSize == 0) + { + return; + } + + if (_map != IntPtr.Zero) + { + data[..dataSize].CopyTo(new Span((void*)(_map + offset), dataSize)); + } + } + + public void SetDataUnchecked(int offset, ReadOnlySpan data) where T : unmanaged + { + SetDataUnchecked(offset, MemoryMarshal.AsBytes(data)); + } + + public static void Copy( + Pipeline pipeline, + CommandBufferScoped cbs, + Auto src, + Auto dst, + int srcOffset, + int dstOffset, + int size, + bool registerSrcUsage = true) + { + var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value; + var dstbuffer = dst.Get(cbs, dstOffset, size, true).Value; + + pipeline.GetOrCreateBlitEncoder().CopyFromBuffer( + srcBuffer, + (ulong)srcOffset, + dstbuffer, + (ulong)dstOffset, + (ulong)size); + } + + public void WaitForFences() + { + _waitable.WaitForFences(); + } + + public void WaitForFences(int offset, int size) + { + _waitable.WaitForFences(offset, size); + } + + public void Dispose() + { + _buffer.Dispose(); + + _flushLock.EnterWriteLock(); + + ClearFlushFence(); + + _flushLock.ExitWriteLock(); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/BufferInfo.cs b/src/Ryujinx.Graphics.Metal/BufferInfo.cs deleted file mode 100644 index b4a1b2cb5..000000000 --- a/src/Ryujinx.Graphics.Metal/BufferInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Metal -{ - public struct BufferInfo - { - public IntPtr Handle; - public int Offset; - public int Index; - } -} diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs new file mode 100644 index 000000000..8f6c2daa7 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -0,0 +1,203 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + public readonly struct ScopedTemporaryBuffer : IDisposable + { + private readonly BufferManager _bufferManager; + private readonly bool _isReserved; + + public readonly BufferRange Range; + public readonly BufferHolder Holder; + + public BufferHandle Handle => Range.Handle; + public int Offset => Range.Offset; + + public ScopedTemporaryBuffer(BufferManager bufferManager, BufferHolder holder, BufferHandle handle, int offset, int size, bool isReserved) + { + _bufferManager = bufferManager; + + Range = new BufferRange(handle, offset, size); + Holder = holder; + + _isReserved = isReserved; + } + + public void Dispose() + { + if (!_isReserved) + { + _bufferManager.Delete(Range.Handle); + } + } + } + + [SupportedOSPlatform("macos")] + public class BufferManager : IDisposable + { + private readonly IdList _buffers; + + private readonly MTLDevice _device; + private readonly MetalRenderer _renderer; + private readonly Pipeline _pipeline; + + public int BufferCount { get; private set; } + + public StagingBuffer StagingBuffer { get; } + + public BufferManager(MTLDevice device, MetalRenderer renderer, Pipeline pipeline) + { + _device = device; + _renderer = renderer; + _pipeline = pipeline; + _buffers = new IdList(); + + StagingBuffer = new StagingBuffer(_renderer, _pipeline, this); + } + + public BufferHandle Create(nint pointer, int size) + { + var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); + + if (buffer == IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}, and pointer 0x{pointer:X}."); + + return BufferHandle.Null; + } + + var holder = new BufferHolder(_renderer, _pipeline, buffer, size); + + BufferCount++; + + ulong handle64 = (uint)_buffers.Add(holder); + + return Unsafe.As(ref handle64); + } + + public BufferHandle CreateWithHandle(int size) + { + return CreateWithHandle(size, out _); + } + + public BufferHandle CreateWithHandle(int size, out BufferHolder holder) + { + holder = Create(size); + + if (holder == null) + { + return BufferHandle.Null; + } + + BufferCount++; + + ulong handle64 = (uint)_buffers.Add(holder); + + return Unsafe.As(ref handle64); + } + + public ScopedTemporaryBuffer ReserveOrCreate(CommandBufferScoped cbs, int size) + { + StagingBufferReserved? result = StagingBuffer.TryReserveData(cbs, size); + + if (result.HasValue) + { + return new ScopedTemporaryBuffer(this, result.Value.Buffer, StagingBuffer.Handle, result.Value.Offset, result.Value.Size, true); + } + else + { + // Create a temporary buffer. + BufferHandle handle = CreateWithHandle(size, out BufferHolder holder); + + return new ScopedTemporaryBuffer(this, holder, handle, 0, size, false); + } + } + + public BufferHolder Create(int size) + { + var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); + + if (buffer != IntPtr.Zero) + { + return new BufferHolder(_renderer, _pipeline, buffer, size); + } + + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}."); + + return null; + } + + public Auto GetBuffer(BufferHandle handle, int offset, int size, bool isWrite) + { + if (TryGetBuffer(handle, out var holder)) + { + return holder.GetBuffer(offset, size, isWrite); + } + + return null; + } + + public Auto GetBuffer(BufferHandle handle, bool isWrite) + { + if (TryGetBuffer(handle, out var holder)) + { + return holder.GetBuffer(isWrite); + } + + return null; + } + + public PinnedSpan GetData(BufferHandle handle, int offset, int size) + { + if (TryGetBuffer(handle, out var holder)) + { + return holder.GetData(offset, size); + } + + return new PinnedSpan(); + } + + public void SetData(BufferHandle handle, int offset, ReadOnlySpan data) where T : unmanaged + { + SetData(handle, offset, MemoryMarshal.Cast(data), null, null); + } + + public void SetData(BufferHandle handle, int offset, ReadOnlySpan data, CommandBufferScoped? cbs, Action endRenderPass) + { + if (TryGetBuffer(handle, out var holder)) + { + holder.SetData(offset, data, cbs, endRenderPass); + } + } + + public void Delete(BufferHandle handle) + { + if (TryGetBuffer(handle, out var holder)) + { + holder.Dispose(); + _buffers.Remove((int)Unsafe.As(ref handle)); + } + } + + private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder) + { + return _buffers.TryGetValue((int)Unsafe.As(ref handle), out holder); + } + + public void Dispose() + { + StagingBuffer.Dispose(); + + foreach (var buffer in _buffers) + { + buffer.Dispose(); + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs b/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs new file mode 100644 index 000000000..379e27407 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs @@ -0,0 +1,85 @@ +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal class BufferUsageBitmap + { + private readonly BitMap _bitmap; + private readonly int _size; + private readonly int _granularity; + private readonly int _bits; + private readonly int _writeBitOffset; + + private readonly int _intsPerCb; + private readonly int _bitsPerCb; + + public BufferUsageBitmap(int size, int granularity) + { + _size = size; + _granularity = granularity; + + // There are two sets of bits - one for read tracking, and the other for write. + int bits = (size + (granularity - 1)) / granularity; + _writeBitOffset = bits; + _bits = bits << 1; + + _intsPerCb = (_bits + (BitMap.IntSize - 1)) / BitMap.IntSize; + _bitsPerCb = _intsPerCb * BitMap.IntSize; + + _bitmap = new BitMap(_bitsPerCb * CommandBufferPool.MaxCommandBuffers); + } + + public void Add(int cbIndex, int offset, int size, bool write) + { + if (size == 0) + { + return; + } + + // Some usages can be out of bounds (vertex buffer on amd), so bound if necessary. + if (offset + size > _size) + { + size = _size - offset; + } + + int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0); + int start = cbBase + offset / _granularity; + int end = cbBase + (offset + size - 1) / _granularity; + + _bitmap.SetRange(start, end); + } + + public bool OverlapsWith(int cbIndex, int offset, int size, bool write = false) + { + if (size == 0) + { + return false; + } + + int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0); + int start = cbBase + offset / _granularity; + int end = cbBase + (offset + size - 1) / _granularity; + + return _bitmap.IsSet(start, end); + } + + public bool OverlapsWith(int offset, int size, bool write) + { + for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++) + { + if (OverlapsWith(i, offset, size, write)) + { + return true; + } + } + + return false; + } + + public void Clear(int cbIndex) + { + _bitmap.ClearInt(cbIndex * _intsPerCb, (cbIndex + 1) * _intsPerCb - 1); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs new file mode 100644 index 000000000..aa659775a --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs @@ -0,0 +1,275 @@ +using SharpMetal.Metal; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class CommandBufferPool : IDisposable + { + public const int MaxCommandBuffers = 16; + + private readonly int _totalCommandBuffers; + private readonly int _totalCommandBuffersMask; + + private readonly MTLDevice _device; + private readonly MTLCommandQueue _queue; + + [SupportedOSPlatform("macos")] + private struct ReservedCommandBuffer + { + public bool InUse; + public bool InConsumption; + public int SubmissionCount; + public MTLCommandBuffer CommandBuffer; + public FenceHolder Fence; + + public List Dependants; + public List Waitables; + + public void Initialize(MTLCommandQueue queue) + { + CommandBuffer = queue.CommandBuffer(); + + Dependants = new List(); + Waitables = new List(); + } + } + + private readonly ReservedCommandBuffer[] _commandBuffers; + + private readonly int[] _queuedIndexes; + private int _queuedIndexesPtr; + private int _queuedCount; + private int _inUseCount; + + public CommandBufferPool(MTLDevice device, MTLCommandQueue queue) + { + _device = device; + _queue = queue; + + _totalCommandBuffers = MaxCommandBuffers; + _totalCommandBuffersMask = _totalCommandBuffers - 1; + + _commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers]; + + _queuedIndexes = new int[_totalCommandBuffers]; + _queuedIndexesPtr = 0; + _queuedCount = 0; + + for (int i = 0; i < _totalCommandBuffers; i++) + { + _commandBuffers[i].Initialize(_queue); + WaitAndDecrementRef(i); + } + } + + public void AddDependant(int cbIndex, IAuto dependant) + { + dependant.IncrementReferenceCount(); + _commandBuffers[cbIndex].Dependants.Add(dependant); + } + + public void AddWaitable(MultiFenceHolder waitable) + { + lock (_commandBuffers) + { + for (int i = 0; i < _totalCommandBuffers; i++) + { + ref var entry = ref _commandBuffers[i]; + + if (entry.InConsumption) + { + AddWaitable(i, waitable); + } + } + } + } + + public void AddInUseWaitable(MultiFenceHolder waitable) + { + lock (_commandBuffers) + { + for (int i = 0; i < _totalCommandBuffers; i++) + { + ref var entry = ref _commandBuffers[i]; + + if (entry.InUse) + { + AddWaitable(i, waitable); + } + } + } + } + + public void AddWaitable(int cbIndex, MultiFenceHolder waitable) + { + ref var entry = ref _commandBuffers[cbIndex]; + if (waitable.AddFence(cbIndex, entry.Fence)) + { + entry.Waitables.Add(waitable); + } + } + + public bool IsFenceOnRentedCommandBuffer(FenceHolder fence) + { + lock (_commandBuffers) + { + for (int i = 0; i < _totalCommandBuffers; i++) + { + ref var entry = ref _commandBuffers[i]; + + if (entry.InUse && entry.Fence == fence) + { + return true; + } + } + } + + return false; + } + + public FenceHolder GetFence(int cbIndex) + { + return _commandBuffers[cbIndex].Fence; + } + + public int GetSubmissionCount(int cbIndex) + { + return _commandBuffers[cbIndex].SubmissionCount; + } + + private int FreeConsumed(bool wait) + { + int freeEntry = 0; + + while (_queuedCount > 0) + { + int index = _queuedIndexes[_queuedIndexesPtr]; + + ref var entry = ref _commandBuffers[index]; + + if (wait || !entry.InConsumption || entry.Fence.IsSignaled()) + { + WaitAndDecrementRef(index); + + wait = false; + freeEntry = index; + + _queuedCount--; + _queuedIndexesPtr = (_queuedIndexesPtr + 1) % _totalCommandBuffers; + } + else + { + break; + } + } + + return freeEntry; + } + + public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs) + { + Return(cbs); + return Rent(); + } + + public CommandBufferScoped Rent() + { + lock (_commandBuffers) + { + int cursor = FreeConsumed(_inUseCount + _queuedCount == _totalCommandBuffers); + + for (int i = 0; i < _totalCommandBuffers; i++) + { + ref var entry = ref _commandBuffers[cursor]; + + if (!entry.InUse && !entry.InConsumption) + { + entry.InUse = true; + + _inUseCount++; + + return new CommandBufferScoped(this, entry.CommandBuffer, cursor); + } + + cursor = (cursor + 1) & _totalCommandBuffersMask; + } + } + + throw new InvalidOperationException($"Out of command buffers (In use: {_inUseCount}, queued: {_queuedCount}, total: {_totalCommandBuffers})"); + } + + public void Return(CommandBufferScoped cbs) + { + lock (_commandBuffers) + { + int cbIndex = cbs.CommandBufferIndex; + + ref var entry = ref _commandBuffers[cbIndex]; + + Debug.Assert(entry.InUse); + Debug.Assert(entry.CommandBuffer.NativePtr == cbs.CommandBuffer.NativePtr); + entry.InUse = false; + entry.InConsumption = true; + entry.SubmissionCount++; + _inUseCount--; + + var commandBuffer = entry.CommandBuffer; + commandBuffer.Commit(); + + // Replace entry with new MTLCommandBuffer + entry.Initialize(_queue); + + int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers; + _queuedIndexes[ptr] = cbIndex; + _queuedCount++; + } + } + + private void WaitAndDecrementRef(int cbIndex, bool refreshFence = true) + { + ref var entry = ref _commandBuffers[cbIndex]; + + if (entry.InConsumption) + { + entry.Fence.Wait(); + entry.InConsumption = false; + } + + foreach (var dependant in entry.Dependants) + { + dependant.DecrementReferenceCount(cbIndex); + } + + foreach (var waitable in entry.Waitables) + { + waitable.RemoveFence(cbIndex); + waitable.RemoveBufferUses(cbIndex); + } + + entry.Dependants.Clear(); + entry.Waitables.Clear(); + entry.Fence?.Dispose(); + + if (refreshFence) + { + entry.Fence = new FenceHolder(entry.CommandBuffer); + } + else + { + entry.Fence = null; + } + } + + public void Dispose() + { + for (int i = 0; i < _totalCommandBuffers; i++) + { + WaitAndDecrementRef(i, refreshFence: false); + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs b/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs new file mode 100644 index 000000000..48c4d3a1e --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs @@ -0,0 +1,41 @@ +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public readonly struct CommandBufferScoped : IDisposable + { + private readonly CommandBufferPool _pool; + public MTLCommandBuffer CommandBuffer { get; } + public int CommandBufferIndex { get; } + + public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, int commandBufferIndex) + { + _pool = pool; + CommandBuffer = commandBuffer; + CommandBufferIndex = commandBufferIndex; + } + + public void AddDependant(IAuto dependant) + { + _pool.AddDependant(CommandBufferIndex, dependant); + } + + public void AddWaitable(MultiFenceHolder waitable) + { + _pool.AddWaitable(CommandBufferIndex, waitable); + } + + public FenceHolder GetFence() + { + return _pool.GetFence(CommandBufferIndex); + } + + public void Dispose() + { + _pool?.Return(this); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 3f64c6ee3..e1f858a84 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -16,5 +16,7 @@ namespace Ryujinx.Graphics.Metal public const int MaxVertexLayouts = 31; public const int MaxTextures = 31; public const int MaxSamplers = 16; + + public const int MinResourceAlignment = 16; } } diff --git a/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs b/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs new file mode 100644 index 000000000..cb7760c1e --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs @@ -0,0 +1,26 @@ +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public readonly struct DisposableBuffer : IDisposable + { + public MTLBuffer Value { get; } + + public DisposableBuffer(MTLBuffer buffer) + { + Value = buffer; + } + + public void Dispose() + { + if (Value != IntPtr.Zero) + { + Value.SetPurgeableState(MTLPurgeableState.Empty); + Value.Dispose(); + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index deee78a60..d0d963ae1 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,6 +1,5 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; -using System.Collections.Generic; using System.Linq; using System.Runtime.Versioning; @@ -38,10 +37,10 @@ namespace Ryujinx.Graphics.Metal public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTextures]; public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxSamplers]; - public List UniformBuffers = []; - public List StorageBuffers = []; + public BufferAssignment[] UniformBuffers = []; + public BufferAssignment[] StorageBuffers = []; - public MTLBuffer IndexBuffer = default; + public BufferRange IndexBuffer = default; public MTLIndexType IndexType = MTLIndexType.UInt16; public ulong IndexBufferOffset = 0; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 74bdc4258..641d1e2ac 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -4,8 +4,8 @@ using Ryujinx.Graphics.Shader; using SharpMetal.Metal; using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; +using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment; namespace Ryujinx.Graphics.Metal { @@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Metal struct EncoderStateManager : IDisposable { private readonly Pipeline _pipeline; + private readonly BufferManager _bufferManager; private readonly RenderPipelineCache _renderPipelineCache; private readonly ComputePipelineCache _computePipelineCache; @@ -21,7 +22,7 @@ namespace Ryujinx.Graphics.Metal private EncoderState _currentState = new(); private readonly Stack _backStates = []; - public readonly MTLBuffer IndexBuffer => _currentState.IndexBuffer; + public readonly BufferRange IndexBuffer => _currentState.IndexBuffer; public readonly MTLIndexType IndexType => _currentState.IndexType; public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; public readonly PrimitiveTopology Topology => _currentState.Topology; @@ -30,11 +31,13 @@ namespace Ryujinx.Graphics.Metal // RGBA32F is the biggest format private const int ZeroBufferSize = 4 * 4; - private readonly MTLBuffer _zeroBuffer; + private readonly BufferHandle _zeroBuffer; - public unsafe EncoderStateManager(MTLDevice device, Pipeline pipeline) + public unsafe EncoderStateManager(MTLDevice device, BufferManager bufferManager, Pipeline pipeline) { _pipeline = pipeline; + _bufferManager = bufferManager; + _renderPipelineCache = new(device); _computePipelineCache = new(device); _depthStencilCache = new(device); @@ -43,7 +46,7 @@ namespace Ryujinx.Graphics.Metal byte[] zeros = new byte[ZeroBufferSize]; fixed (byte* ptr = zeros) { - _zeroBuffer = device.NewBuffer((IntPtr)ptr, ZeroBufferSize, MTLResourceOptions.ResourceStorageModeShared); + _zeroBuffer = _bufferManager.Create((IntPtr)ptr, ZeroBufferSize); } } @@ -355,8 +358,7 @@ namespace Ryujinx.Graphics.Metal { _currentState.IndexType = type.Convert(); _currentState.IndexBufferOffset = (ulong)buffer.Offset; - var handle = buffer.Handle; - _currentState.IndexBuffer = new(Unsafe.As(ref handle)); + _currentState.IndexBuffer = buffer; } } @@ -657,20 +659,7 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateUniformBuffers(ReadOnlySpan buffers) { - _currentState.UniformBuffers = []; - - foreach (BufferAssignment buffer in buffers) - { - if (buffer.Range.Size != 0) - { - _currentState.UniformBuffers.Add(new BufferInfo - { - Handle = buffer.Range.Handle.ToIntPtr(), - Offset = buffer.Range.Offset, - Index = buffer.Binding - }); - } - } + _currentState.UniformBuffers = buffers.ToArray(); // Inline update if (_pipeline.CurrentEncoder != null) @@ -691,20 +680,13 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateStorageBuffers(ReadOnlySpan buffers) { - _currentState.StorageBuffers = []; + _currentState.StorageBuffers = buffers.ToArray(); - foreach (BufferAssignment buffer in buffers) + for (int i = 0; i < _currentState.StorageBuffers.Length; i++) { - if (buffer.Range.Size != 0) - { - // TODO: DONT offset the binding by 15 - _currentState.StorageBuffers.Add(new BufferInfo - { - Handle = buffer.Range.Handle.ToIntPtr(), - Offset = buffer.Range.Offset, - Index = buffer.Binding + 15 - }); - } + BufferAssignment buffer = _currentState.StorageBuffers[i]; + // TODO: DONT offset the binding by 15 + _currentState.StorageBuffers[i] = new BufferAssignment(buffer.Binding + 15, buffer.Range); } // Inline update @@ -956,50 +938,51 @@ namespace Ryujinx.Graphics.Metal private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) { - var buffers = new List(); + var buffers = new List(); for (int i = 0; i < bufferDescriptors.Length; i++) { - if (bufferDescriptors[i].Buffer.Handle.ToIntPtr() != IntPtr.Zero) - { - buffers.Add(new BufferInfo - { - Handle = bufferDescriptors[i].Buffer.Handle.ToIntPtr(), - Offset = bufferDescriptors[i].Buffer.Offset, - Index = i - }); - } + buffers.Add(new BufferAssignment(i, bufferDescriptors[i].Buffer)); } // Zero buffer - buffers.Add(new BufferInfo - { - Handle = _zeroBuffer.NativePtr, - Offset = 0, - Index = bufferDescriptors.Length - }); + buffers.Add(new BufferAssignment( + bufferDescriptors.Length, + new BufferRange(_zeroBuffer, 0, ZeroBufferSize))); - SetRenderBuffers(renderCommandEncoder, buffers); + SetRenderBuffers(renderCommandEncoder, buffers.ToArray()); } - private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) + private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferAssignment[] buffers, bool fragment = false) { foreach (var buffer in buffers) { - renderCommandEncoder.SetVertexBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index); + var range = buffer.Range; + var autoBuffer = _bufferManager.GetBuffer(range.Handle, range.Offset, range.Size, range.Write); - if (fragment) + if (autoBuffer != null) { - renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index); + var mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer).Value; + + renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)range.Offset, (ulong)buffer.Binding); + + if (fragment) + { + renderCommandEncoder.SetFragmentBuffer(mtlBuffer, (ulong)range.Offset, (ulong)buffer.Binding); + } } } } - private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, List buffers) + private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferAssignment[] buffers) { foreach (var buffer in buffers) { - computeCommandEncoder.SetBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index); + var range = buffer.Range; + var mtlBuffer = _bufferManager.GetBuffer(range.Handle, range.Offset, range.Size, range.Write).Get(_pipeline.CurrentCommandBuffer).Value; + + computeCommandEncoder.SetBuffer(mtlBuffer, (ulong)range.Offset, (ulong)buffer.Binding); + } } diff --git a/src/Ryujinx.Graphics.Metal/FenceHolder.cs b/src/Ryujinx.Graphics.Metal/FenceHolder.cs new file mode 100644 index 000000000..3f11fd971 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/FenceHolder.cs @@ -0,0 +1,77 @@ +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; +using System.Threading; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class FenceHolder : IDisposable + { + private MTLCommandBuffer _fence; + private int _referenceCount; + private bool _disposed; + + public FenceHolder(MTLCommandBuffer fence) + { + _fence = fence; + _referenceCount = 1; + } + + public MTLCommandBuffer GetUnsafe() + { + return _fence; + } + + public bool TryGet(out MTLCommandBuffer fence) + { + int lastValue; + do + { + lastValue = _referenceCount; + + if (lastValue == 0) + { + fence = default; + return false; + } + } while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); + + fence = _fence; + return true; + } + + public MTLCommandBuffer Get() + { + Interlocked.Increment(ref _referenceCount); + return _fence; + } + + public void Put() + { + if (Interlocked.Decrement(ref _referenceCount) == 0) + { + _fence = default; + } + } + + public void Wait() + { + _fence.WaitUntilCompleted(); + } + + public bool IsSignaled() + { + return _fence.Status == MTLCommandBufferStatus.Completed; + } + + public void Dispose() + { + if (!_disposed) + { + Put(); + _disposed = true; + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Handle.cs b/src/Ryujinx.Graphics.Metal/Handle.cs deleted file mode 100644 index 82f8dbd5b..000000000 --- a/src/Ryujinx.Graphics.Metal/Handle.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System; -using System.Runtime.CompilerServices; - -namespace Ryujinx.Graphics.Metal -{ - static class Handle - { - public static IntPtr ToIntPtr(this BufferHandle handle) - { - return Unsafe.As(ref handle); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 5066b0244..bed199987 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -10,7 +10,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class HelperShader : IDisposable + public class HelperShader : IDisposable { private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders"; private readonly Pipeline _pipeline; diff --git a/src/Ryujinx.Graphics.Metal/IdList.cs b/src/Ryujinx.Graphics.Metal/IdList.cs new file mode 100644 index 000000000..2c15a80ef --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/IdList.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Metal +{ + class IdList where T : class + { + private readonly List _list; + private int _freeMin; + + public IdList() + { + _list = new List(); + _freeMin = 0; + } + + public int Add(T value) + { + int id; + int count = _list.Count; + id = _list.IndexOf(null, _freeMin); + + if ((uint)id < (uint)count) + { + _list[id] = value; + } + else + { + id = count; + _freeMin = id + 1; + + _list.Add(value); + } + + return id + 1; + } + + public void Remove(int id) + { + id--; + + int count = _list.Count; + + if ((uint)id >= (uint)count) + { + return; + } + + if (id + 1 == count) + { + // Trim unused items. + int removeIndex = id; + + while (removeIndex > 0 && _list[removeIndex - 1] == null) + { + removeIndex--; + } + + _list.RemoveRange(removeIndex, count - removeIndex); + + if (_freeMin > removeIndex) + { + _freeMin = removeIndex; + } + } + else + { + _list[id] = null; + + if (_freeMin > id) + { + _freeMin = id; + } + } + } + + public bool TryGetValue(int id, out T value) + { + id--; + + try + { + if ((uint)id < (uint)_list.Count) + { + value = _list[id]; + return value != null; + } + + value = null; + return false; + } + catch (ArgumentOutOfRangeException) + { + value = null; + return false; + } + catch (IndexOutOfRangeException) + { + value = null; + return false; + } + } + + public void Clear() + { + _list.Clear(); + _freeMin = 0; + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < _list.Count; i++) + { + if (_list[i] != null) + { + yield return _list[i]; + } + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index f9ed0423b..1f61090f0 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -2,11 +2,9 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader.Translation; -using SharpMetal.Foundation; using SharpMetal.Metal; using SharpMetal.QuartzCore; using System; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -19,12 +17,20 @@ namespace Ryujinx.Graphics.Metal private readonly Func _getMetalLayer; private Pipeline _pipeline; + private HelperShader _helperShader; + private BufferManager _bufferManager; private Window _window; + private CommandBufferPool _commandBufferPool; public event EventHandler ScreenCaptured; public bool PreferThreading => true; public IPipeline Pipeline => _pipeline; public IWindow Window => _window; + public HelperShader HelperShader => _helperShader; + public BufferManager BufferManager => _bufferManager; + public CommandBufferPool CommandBufferPool => _commandBufferPool; + public Action InterruptAction { get; private set; } + public SyncManager SyncManager { get; private set; } public MetalRenderer(Func metalLayer) { @@ -35,7 +41,7 @@ namespace Ryujinx.Graphics.Metal throw new NotSupportedException("Metal backend requires Tier 2 Argument Buffer support."); } - _queue = _device.NewCommandQueue(); + _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers); _getMetalLayer = metalLayer; } @@ -45,8 +51,15 @@ namespace Ryujinx.Graphics.Metal layer.Device = _device; layer.FramebufferOnly = false; + _commandBufferPool = new CommandBufferPool(_device, _queue); _window = new Window(this, layer); - _pipeline = new Pipeline(_device, _queue); + _pipeline = new Pipeline(_device, this, _queue); + _bufferManager = new BufferManager(_device, this, _pipeline); + + _pipeline.InitEncoderStateManager(_bufferManager); + + _helperShader = new HelperShader(_device, _pipeline); + SyncManager = new SyncManager(this); } public void BackgroundContextAction(Action action, bool alwaysBackground = false) @@ -54,11 +67,14 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } + public BufferHandle CreateBuffer(int size, BufferAccess access) + { + return _bufferManager.CreateWithHandle(size); + } + public BufferHandle CreateBuffer(IntPtr pointer, int size) { - var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); - var bufferPtr = buffer.NativePtr; - return Unsafe.As(ref bufferPtr); + return _bufferManager.Create(pointer, size); } public BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers) @@ -71,15 +87,6 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public BufferHandle CreateBuffer(int size, BufferAccess access) - { - var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); - buffer.SetPurgeableState(MTLPurgeableState.NonVolatile); - - var bufferPtr = buffer.NativePtr; - return Unsafe.As(ref bufferPtr); - } - public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { return new Program(shaders, _device); @@ -94,10 +101,10 @@ namespace Ryujinx.Graphics.Metal { if (info.Target == Target.TextureBuffer) { - return new TextureBuffer(_device, _pipeline, info); + return new TextureBuffer(this, info); } - return new Texture(_device, _pipeline, info); + return new Texture(_device, this, _pipeline, info); } public ITextureArray CreateTextureArray(int size, bool isBuffer) @@ -113,19 +120,17 @@ namespace Ryujinx.Graphics.Metal public void CreateSync(ulong id, bool strict) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + SyncManager.Create(id, strict); } public void DeleteBuffer(BufferHandle buffer) { - MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); - mtlBuffer.SetPurgeableState(MTLPurgeableState.Empty); + _bufferManager.Delete(buffer); } - public unsafe PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) + public PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) { - MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); - return new PinnedSpan(IntPtr.Add(mtlBuffer.Contents, offset).ToPointer(), size); + return _bufferManager.GetData(buffer, offset, size); } public Capabilities GetCapabilities() @@ -198,8 +203,7 @@ namespace Ryujinx.Graphics.Metal public ulong GetCurrentSync() { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - return 0; + return SyncManager.GetCurrent(); } public HardwareInfo GetHardwareInfo() @@ -212,18 +216,9 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public unsafe void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) + public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) { - var blitEncoder = _pipeline.GetOrCreateBlitEncoder(); - - using MTLBuffer src = _device.NewBuffer((ulong)data.Length, MTLResourceOptions.ResourceStorageModeManaged); - { - var span = new Span(src.Contents.ToPointer(), data.Length); - data.CopyTo(span); - - MTLBuffer dst = new(Unsafe.As(ref buffer)); - blitEncoder.CopyFromBuffer(src, 0, dst, (ulong)offset, (ulong)data.Length); - } + _bufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPassDelegate); } public void UpdateCounters() @@ -233,7 +228,7 @@ namespace Ryujinx.Graphics.Metal public void PreFrame() { - + SyncManager.Cleanup(); } public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, float divisor, bool hostReserved) @@ -251,12 +246,25 @@ namespace Ryujinx.Graphics.Metal public void WaitSync(ulong id) { - throw new NotImplementedException(); + SyncManager.Wait(id); + } + + public void FlushAllCommands() + { + _pipeline.FlushCommandsImpl(); + } + + public void RegisterFlush() + { + SyncManager.RegisterFlush(); + + // Periodically free unused regions of the staging buffer to avoid doing it all at once. + _bufferManager.StagingBuffer.FreeCompleted(); } public void SetInterruptAction(Action interruptAction) { - // Not needed for now + InterruptAction = interruptAction; } public void Screenshot() diff --git a/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs new file mode 100644 index 000000000..481580b8e --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs @@ -0,0 +1,262 @@ +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + /// + /// Holder for multiple host GPU fences. + /// + [SupportedOSPlatform("macos")] + public class MultiFenceHolder + { + private const int BufferUsageTrackingGranularity = 4096; + + private readonly FenceHolder[] _fences; + private readonly BufferUsageBitmap _bufferUsageBitmap; + + /// + /// Creates a new instance of the multiple fence holder. + /// + public MultiFenceHolder() + { + _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; + } + + /// + /// Creates a new instance of the multiple fence holder, with a given buffer size in mind. + /// + /// Size of the buffer + public MultiFenceHolder(int size) + { + _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; + _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); + } + + /// + /// Adds read/write buffer usage information to the uses list. + /// + /// Index of the command buffer where the buffer is used + /// Offset of the buffer being used + /// Size of the buffer region being used, in bytes + /// Whether the access is a write or not + public void AddBufferUse(int cbIndex, int offset, int size, bool write) + { + _bufferUsageBitmap.Add(cbIndex, offset, size, false); + + if (write) + { + _bufferUsageBitmap.Add(cbIndex, offset, size, true); + } + } + + /// + /// Removes all buffer usage information for a given command buffer. + /// + /// Index of the command buffer where the buffer is used + public void RemoveBufferUses(int cbIndex) + { + _bufferUsageBitmap?.Clear(cbIndex); + } + + /// + /// Checks if a given range of a buffer is being used by a command buffer still being processed by the GPU. + /// + /// Index of the command buffer where the buffer is used + /// Offset of the buffer being used + /// Size of the buffer region being used, in bytes + /// True if in use, false otherwise + public bool IsBufferRangeInUse(int cbIndex, int offset, int size) + { + return _bufferUsageBitmap.OverlapsWith(cbIndex, offset, size); + } + + /// + /// Checks if a given range of a buffer is being used by any command buffer still being processed by the GPU. + /// + /// Offset of the buffer being used + /// Size of the buffer region being used, in bytes + /// True if only write usages should count + /// True if in use, false otherwise + public bool IsBufferRangeInUse(int offset, int size, bool write) + { + return _bufferUsageBitmap.OverlapsWith(offset, size, write); + } + + /// + /// Adds a fence to the holder. + /// + /// Command buffer index of the command buffer that owns the fence + /// Fence to be added + /// True if the command buffer's previous fence value was null + public bool AddFence(int cbIndex, FenceHolder fence) + { + ref FenceHolder fenceRef = ref _fences[cbIndex]; + + if (fenceRef == null) + { + fenceRef = fence; + return true; + } + + return false; + } + + /// + /// Removes a fence from the holder. + /// + /// Command buffer index of the command buffer that owns the fence + public void RemoveFence(int cbIndex) + { + _fences[cbIndex] = null; + } + + /// + /// Determines if a fence referenced on the given command buffer. + /// + /// Index of the command buffer to check if it's used + /// True if referenced, false otherwise + public bool HasFence(int cbIndex) + { + return _fences[cbIndex] != null; + } + + /// + /// Wait until all the fences on the holder are signaled. + /// + public void WaitForFences() + { + WaitForFencesImpl(0, 0, true); + } + + /// + /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. + /// + /// Start offset of the buffer range + /// Size of the buffer range in bytes + public void WaitForFences(int offset, int size) + { + WaitForFencesImpl(offset, size, true); + } + + /// + /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. + /// + + // TODO: Add a proper timeout! + public bool WaitForFences(bool indefinite) + { + return WaitForFencesImpl(0, 0, indefinite); + } + + /// + /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. + /// + /// Start offset of the buffer range + /// Size of the buffer range in bytes + /// Indicates if this should wait indefinitely + /// True if all fences were signaled before the timeout expired, false otherwise + private bool WaitForFencesImpl(int offset, int size, bool indefinite) + { + Span fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; + + int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders); + Span fences = stackalloc MTLCommandBuffer[count]; + + int fenceCount = 0; + + for (int i = 0; i < count; i++) + { + if (fenceHolders[i].TryGet(out MTLCommandBuffer fence)) + { + fences[fenceCount] = fence; + + if (fenceCount < i) + { + fenceHolders[fenceCount] = fenceHolders[i]; + } + + fenceCount++; + } + } + + if (fenceCount == 0) + { + return true; + } + + bool signaled = true; + + if (indefinite) + { + foreach (var fence in fences) + { + fence.WaitUntilCompleted(); + } + } + else + { + foreach (var fence in fences) + { + if (fence.Status != MTLCommandBufferStatus.Completed) + { + signaled = false; + } + } + } + + for (int i = 0; i < fenceCount; i++) + { + fenceHolders[i].Put(); + } + + return signaled; + } + + /// + /// Gets fences to wait for. + /// + /// Span to store fences in + /// Number of fences placed in storage + private int GetFences(Span storage) + { + int count = 0; + + for (int i = 0; i < _fences.Length; i++) + { + var fence = _fences[i]; + + if (fence != null) + { + storage[count++] = fence; + } + } + + return count; + } + + /// + /// Gets fences to wait for use of a given buffer region. + /// + /// Span to store overlapping fences in + /// Offset of the range + /// Size of the range in bytes + /// Number of fences for the specified region placed in storage + private int GetOverlappingFences(Span storage, int offset, int size) + { + int count = 0; + + for (int i = 0; i < _fences.Length; i++) + { + var fence = _fences[i]; + + if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size)) + { + storage[count++] = fence; + } + } + + return count; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 34c4d30cb..9e2e4f26f 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -5,12 +5,11 @@ using SharpMetal.Foundation; using SharpMetal.Metal; using SharpMetal.QuartzCore; using System; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - enum EncoderType + public enum EncoderType { Blit, Compute, @@ -19,14 +18,19 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - class Pipeline : IPipeline, IDisposable + public class Pipeline : IPipeline, IDisposable { private readonly MTLDevice _device; private readonly MTLCommandQueue _commandQueue; - private readonly HelperShader _helperShader; + private readonly MetalRenderer _renderer; - private MTLCommandBuffer _commandBuffer; - public MTLCommandBuffer CommandBuffer => _commandBuffer; + private CommandBufferScoped Cbs; + private CommandBufferScoped? PreloadCbs; + public MTLCommandBuffer CommandBuffer; + + public readonly Action EndRenderPassDelegate; + + public CommandBufferScoped CurrentCommandBuffer => Cbs; private MTLCommandEncoder? _currentEncoder; public MTLCommandEncoder? CurrentEncoder => _currentEncoder; @@ -36,14 +40,20 @@ namespace Ryujinx.Graphics.Metal private EncoderStateManager _encoderStateManager; - public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) + public Pipeline(MTLDevice device, MetalRenderer renderer, MTLCommandQueue commandQueue) { _device = device; + _renderer = renderer; _commandQueue = commandQueue; - _helperShader = new HelperShader(_device, this); - _commandBuffer = _commandQueue.CommandBuffer(); - _encoderStateManager = new EncoderStateManager(_device, this); + EndRenderPassDelegate = EndCurrentPass; + + CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer; + } + + public void InitEncoderStateManager(BufferManager bufferManager) + { + _encoderStateManager = new EncoderStateManager(_device, bufferManager, this); } public void SaveState() @@ -156,7 +166,7 @@ namespace Ryujinx.Graphics.Metal EndCurrentPass(); var descriptor = new MTLBlitPassDescriptor(); - var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); + var blitCommandEncoder = Cbs.CommandBuffer.BlitCommandEncoder(descriptor); _currentEncoder = blitCommandEncoder; _currentEncoderType = EncoderType.Blit; @@ -178,21 +188,35 @@ namespace Ryujinx.Graphics.Metal { // TODO: Clean this up var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); - var dst = new Texture(_device, this, textureInfo, drawable.Texture, 0, 0); + var dst = new Texture(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0); - _helperShader.BlitColor(src, dst, srcRegion, dstRegion, isLinear); + _renderer.HelperShader.BlitColor(src, dst, srcRegion, dstRegion, isLinear); EndCurrentPass(); - _commandBuffer.PresentDrawable(drawable); - _commandBuffer.Commit(); + Cbs.CommandBuffer.PresentDrawable(drawable); - _commandBuffer = _commandQueue.CommandBuffer(); + CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; + + // TODO: Auto flush counting + _renderer.SyncManager.GetAndResetWaitTicks(); // Cleanup dst.Dispose(); } + public void FlushCommandsImpl() + { + SaveState(); + + EndCurrentPass(); + + CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; + _renderer.RegisterFlush(); + + RestoreState(); + } + public void BlitColor( ITexture src, ITexture dst, @@ -200,7 +224,7 @@ namespace Ryujinx.Graphics.Metal Extents2D dstRegion, bool linearFilter) { - _helperShader.BlitColor(src, dst, srcRegion, dstRegion, linearFilter); + _renderer.HelperShader.BlitColor(src, dst, srcRegion, dstRegion, linearFilter); } public void Barrier() @@ -235,9 +259,10 @@ namespace Ryujinx.Graphics.Metal { var blitCommandEncoder = GetOrCreateBlitEncoder(); + var mtlBuffer = _renderer.BufferManager.GetBuffer(destination, offset, size, true).Get(Cbs, offset, size, true).Value; + // Might need a closer look, range's count, lower, and upper bound // must be a multiple of 4 - MTLBuffer mtlBuffer = new(Unsafe.As(ref destination)); blitCommandEncoder.FillBuffer(mtlBuffer, new NSRange { @@ -259,7 +284,7 @@ namespace Ryujinx.Graphics.Metal return; } - _helperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height); + _renderer.HelperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) @@ -273,7 +298,7 @@ namespace Ryujinx.Graphics.Metal return; } - _helperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height); + _renderer.HelperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height); } public void CommandBufferBarrier() @@ -281,19 +306,12 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) + public void CopyBuffer(BufferHandle src, BufferHandle dst, int srcOffset, int dstOffset, int size) { - var blitCommandEncoder = GetOrCreateBlitEncoder(); + var srcBuffer = _renderer.BufferManager.GetBuffer(src, srcOffset, size, false); + var dstBuffer = _renderer.BufferManager.GetBuffer(dst, dstOffset, size, true); - MTLBuffer sourceBuffer = new(Unsafe.As(ref source)); - MTLBuffer destinationBuffer = new(Unsafe.As(ref destination)); - - blitCommandEncoder.CopyFromBuffer( - sourceBuffer, - (ulong)srcOffset, - destinationBuffer, - (ulong)dstOffset, - (ulong)size); + BufferHolder.Copy(this, Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size); } public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) @@ -327,11 +345,13 @@ namespace Ryujinx.Graphics.Metal // TODO: Support topology re-indexing to provide support for TriangleFans var primitiveType = _encoderStateManager.Topology.Convert(); + var indexBuffer = _renderer.BufferManager.GetBuffer(_encoderStateManager.IndexBuffer.Handle, false); + renderCommandEncoder.DrawIndexedPrimitives( primitiveType, (ulong)indexCount, _encoderStateManager.IndexType, - _encoderStateManager.IndexBuffer, + indexBuffer.Get(Cbs, 0, indexCount * sizeof(int)).Value, _encoderStateManager.IndexBufferOffset, (ulong)instanceCount, firstVertex, @@ -368,7 +388,7 @@ namespace Ryujinx.Graphics.Metal public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { - _helperShader.DrawTexture(texture, sampler, srcRegion, dstRegion); + _renderer.HelperShader.DrawTexture(texture, sampler, srcRegion, dstRegion); } public void SetAlphaTest(bool enable, float reference, CompareOp op) diff --git a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs new file mode 100644 index 000000000..81adcd116 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs @@ -0,0 +1,294 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + public readonly struct StagingBufferReserved + { + public readonly BufferHolder Buffer; + public readonly int Offset; + public readonly int Size; + + public StagingBufferReserved(BufferHolder buffer, int offset, int size) + { + Buffer = buffer; + Offset = offset; + Size = size; + } + } + + [SupportedOSPlatform("macos")] + public class StagingBuffer : IDisposable + { + private const int BufferSize = 32 * 1024 * 1024; + + private int _freeOffset; + private int _freeSize; + + private readonly MetalRenderer _renderer; + private readonly Pipeline _pipeline; + private readonly BufferHolder _buffer; + private readonly int _resourceAlignment; + + public readonly BufferHandle Handle; + + private readonly struct PendingCopy + { + public FenceHolder Fence { get; } + public int Size { get; } + + public PendingCopy(FenceHolder fence, int size) + { + Fence = fence; + Size = size; + fence.Get(); + } + } + + private readonly Queue _pendingCopies; + + public StagingBuffer(MetalRenderer renderer, Pipeline pipeline, BufferManager bufferManager) + { + _renderer = renderer; + _pipeline = pipeline; + + Handle = bufferManager.CreateWithHandle(BufferSize, out _buffer); + _pendingCopies = new Queue(); + _freeSize = BufferSize; + _resourceAlignment = Constants.MinResourceAlignment; + } + + public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan data) + { + bool isRender = cbs != null; + CommandBufferScoped scoped = cbs ?? cbp.Rent(); + + // Must push all data to the buffer. If it can't fit, split it up. + + endRenderPass?.Invoke(); + + while (data.Length > 0) + { + if (_freeSize < data.Length) + { + FreeCompleted(); + } + + while (_freeSize == 0) + { + if (!WaitFreeCompleted(cbp)) + { + if (isRender) + { + _renderer.FlushAllCommands(); + scoped = cbp.Rent(); + isRender = false; + } + else + { + scoped = cbp.ReturnAndRent(scoped); + } + } + } + + int chunkSize = Math.Min(_freeSize, data.Length); + + PushDataImpl(scoped, dst, dstOffset, data[..chunkSize]); + + dstOffset += chunkSize; + data = data[chunkSize..]; + } + + if (!isRender) + { + scoped.Dispose(); + } + } + + private void PushDataImpl(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) + { + var srcBuffer = _buffer.GetBuffer(); + var dstBuffer = dst.GetBuffer(dstOffset, data.Length, true); + + int offset = _freeOffset; + int capacity = BufferSize - offset; + if (capacity < data.Length) + { + _buffer.SetDataUnchecked(offset, data[..capacity]); + _buffer.SetDataUnchecked(0, data[capacity..]); + + BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity); + BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity); + } + else + { + _buffer.SetDataUnchecked(offset, data); + + BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length); + } + + _freeOffset = (offset + data.Length) & (BufferSize - 1); + _freeSize -= data.Length; + Debug.Assert(_freeSize >= 0); + + _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length)); + } + + public bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan data) + { + if (data.Length > BufferSize) + { + return false; + } + + if (_freeSize < data.Length) + { + FreeCompleted(); + + if (_freeSize < data.Length) + { + return false; + } + } + + endRenderPass?.Invoke(); + + PushDataImpl(cbs, dst, dstOffset, data); + + return true; + } + + private StagingBufferReserved ReserveDataImpl(CommandBufferScoped cbs, int size, int alignment) + { + // Assumes the caller has already determined that there is enough space. + int offset = BitUtils.AlignUp(_freeOffset, alignment); + int padding = offset - _freeOffset; + + int capacity = Math.Min(_freeSize, BufferSize - offset); + int reservedLength = size + padding; + if (capacity < size) + { + offset = 0; // Place at start. + reservedLength += capacity; + } + + _freeOffset = (_freeOffset + reservedLength) & (BufferSize - 1); + _freeSize -= reservedLength; + Debug.Assert(_freeSize >= 0); + + _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), reservedLength)); + + return new StagingBufferReserved(_buffer, offset, size); + } + + private int GetContiguousFreeSize(int alignment) + { + int alignedFreeOffset = BitUtils.AlignUp(_freeOffset, alignment); + int padding = alignedFreeOffset - _freeOffset; + + // Free regions: + // - Aligned free offset to end (minimum free size - padding) + // - 0 to _freeOffset + freeSize wrapped (only if free area contains 0) + + int endOffset = (_freeOffset + _freeSize) & (BufferSize - 1); + + return Math.Max( + Math.Min(_freeSize - padding, BufferSize - alignedFreeOffset), + endOffset <= _freeOffset ? Math.Min(_freeSize, endOffset) : 0 + ); + } + + /// + /// Reserve a range on the staging buffer for the current command buffer and upload data to it. + /// + /// Command buffer to reserve the data on + /// The minimum size the reserved data requires + /// The required alignment for the buffer offset + /// The reserved range of the staging buffer + public StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size, int alignment) + { + if (size > BufferSize) + { + return null; + } + + // Temporary reserved data cannot be fragmented. + + if (GetContiguousFreeSize(alignment) < size) + { + FreeCompleted(); + + if (GetContiguousFreeSize(alignment) < size) + { + Logger.Debug?.PrintMsg(LogClass.Gpu, $"Staging buffer out of space to reserve data of size {size}."); + return null; + } + } + + return ReserveDataImpl(cbs, size, alignment); + } + + /// + /// Reserve a range on the staging buffer for the current command buffer and upload data to it. + /// Uses the most permissive byte alignment. + /// + /// Command buffer to reserve the data on + /// The minimum size the reserved data requires + /// The reserved range of the staging buffer + public StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size) + { + return TryReserveData(cbs, size, _resourceAlignment); + } + + private bool WaitFreeCompleted(CommandBufferPool cbp) + { + if (_pendingCopies.TryPeek(out var pc)) + { + if (!pc.Fence.IsSignaled()) + { + if (cbp.IsFenceOnRentedCommandBuffer(pc.Fence)) + { + return false; + } + + pc.Fence.Wait(); + } + + var dequeued = _pendingCopies.Dequeue(); + Debug.Assert(dequeued.Fence == pc.Fence); + _freeSize += pc.Size; + pc.Fence.Put(); + } + + return true; + } + + public void FreeCompleted() + { + FenceHolder signalledFence = null; + while (_pendingCopies.TryPeek(out var pc) && (pc.Fence == signalledFence || pc.Fence.IsSignaled())) + { + signalledFence = pc.Fence; // Already checked - don't need to do it again. + var dequeued = _pendingCopies.Dequeue(); + Debug.Assert(dequeued.Fence == pc.Fence); + _freeSize += pc.Size; + pc.Fence.Put(); + } + } + + public void Dispose() + { + _renderer.BufferManager.Delete(Handle); + + while (_pendingCopies.TryDequeue(out var pc)) + { + pc.Fence.Put(); + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/SyncManager.cs b/src/Ryujinx.Graphics.Metal/SyncManager.cs new file mode 100644 index 000000000..528531575 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/SyncManager.cs @@ -0,0 +1,214 @@ +using Ryujinx.Common.Logging; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class SyncManager + { + private class SyncHandle + { + public ulong ID; + public MultiFenceHolder Waitable; + public ulong FlushId; + public bool Signalled; + + public bool NeedsFlush(ulong currentFlushId) + { + return (long)(FlushId - currentFlushId) >= 0; + } + } + + private ulong _firstHandle; + + private readonly MetalRenderer _renderer; + private readonly List _handles; + private ulong _flushId; + private long _waitTicks; + + public SyncManager(MetalRenderer renderer) + { + _renderer = renderer; + _handles = new List(); + } + + public void RegisterFlush() + { + _flushId++; + } + + public void Create(ulong id, bool strict) + { + ulong flushId = _flushId; + MultiFenceHolder waitable = new(); + if (strict || _renderer.InterruptAction == null) + { + _renderer.FlushAllCommands(); + _renderer.CommandBufferPool.AddWaitable(waitable); + } + else + { + // Don't flush commands, instead wait for the current command buffer to finish. + // If this sync is waited on before the command buffer is submitted, interrupt the gpu thread and flush it manually. + + _renderer.CommandBufferPool.AddInUseWaitable(waitable); + } + + SyncHandle handle = new() + { + ID = id, + Waitable = waitable, + FlushId = flushId, + }; + + lock (_handles) + { + _handles.Add(handle); + } + } + + public ulong GetCurrent() + { + lock (_handles) + { + ulong lastHandle = _firstHandle; + + foreach (SyncHandle handle in _handles) + { + lock (handle) + { + if (handle.Waitable == null) + { + continue; + } + + if (handle.ID > lastHandle) + { + bool signaled = handle.Signalled || handle.Waitable.WaitForFences(false); + if (signaled) + { + lastHandle = handle.ID; + handle.Signalled = true; + } + } + } + } + + return lastHandle; + } + } + + public void Wait(ulong id) + { + SyncHandle result = null; + + lock (_handles) + { + if ((long)(_firstHandle - id) > 0) + { + return; // The handle has already been signalled or deleted. + } + + foreach (SyncHandle handle in _handles) + { + if (handle.ID == id) + { + result = handle; + break; + } + } + } + + if (result != null) + { + if (result.Waitable == null) + { + return; + } + + long beforeTicks = Stopwatch.GetTimestamp(); + + if (result.NeedsFlush(_flushId)) + { + _renderer.InterruptAction(() => + { + if (result.NeedsFlush(_flushId)) + { + _renderer.FlushAllCommands(); + } + }); + } + + lock (result) + { + if (result.Waitable == null) + { + return; + } + + bool signaled = result.Signalled || result.Waitable.WaitForFences(false); + + if (!signaled) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); + } + else + { + _waitTicks += Stopwatch.GetTimestamp() - beforeTicks; + result.Signalled = true; + } + } + } + } + + public void Cleanup() + { + // Iterate through handles and remove any that have already been signalled. + + while (true) + { + SyncHandle first = null; + lock (_handles) + { + first = _handles.FirstOrDefault(); + } + + if (first == null || first.NeedsFlush(_flushId)) + { + break; + } + + bool signaled = first.Waitable.WaitForFences(false); + if (signaled) + { + // Delete the sync object. + lock (_handles) + { + lock (first) + { + _firstHandle = first.ID + 1; + _handles.RemoveAt(0); + first.Waitable = null; + } + } + } + else + { + // This sync handle and any following have not been reached yet. + break; + } + } + } + + public long GetAndResetWaitTicks() + { + long result = _waitTicks; + _waitTicks = 0; + + return result; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 4ec5773bf..7d56375a6 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -3,15 +3,14 @@ using SharpMetal.Foundation; using SharpMetal.Metal; using System; using System.Buffers; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class Texture : TextureBase, ITexture + public class Texture : TextureBase, ITexture { - public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) : base(device, pipeline, info) + public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) { var descriptor = new MTLTextureDescriptor { @@ -38,7 +37,7 @@ namespace Ryujinx.Graphics.Metal _mtlTexture = _device.NewTexture(descriptor); } - public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, pipeline, info) + public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info) { var pixelFormat = FormatTable.GetFormat(Info.Format); var textureType = Info.Target.Convert(); @@ -168,6 +167,9 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(BufferRange range, int layer, int level, int stride) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + var cbs = _pipeline.CurrentCommandBuffer; + + int outSize = Info.GetMipSize(level); ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; @@ -176,8 +178,8 @@ namespace Ryujinx.Graphics.Metal bytesPerImage = bytesPerRow * (ulong)Info.Height; } - var handle = range.Handle; - MTLBuffer mtlBuffer = new(Unsafe.As(ref handle)); + var autoBuffer = _renderer.BufferManager.GetBuffer(range.Handle, true); + var mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; blitCommandEncoder.CopyFromTexture( _mtlTexture, @@ -193,7 +195,7 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - return new Texture(_device, _pipeline, info, _mtlTexture, firstLayer, firstLevel); + return new Texture(_device, _renderer, _pipeline, info, _mtlTexture, firstLayer, firstLevel); } public PinnedSpan GetData() @@ -215,6 +217,7 @@ namespace Ryujinx.Graphics.Metal unsafe { + var mtlBuffer = _device.NewBuffer(length, MTLResourceOptions.ResourceStorageModeShared); blitCommandEncoder.CopyFromTexture( diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index 8bd5c9513..4b71ec9f9 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -6,13 +6,14 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - abstract class TextureBase : IDisposable + public abstract class TextureBase : IDisposable { private bool _disposed; protected readonly TextureCreateInfo _info; protected readonly Pipeline _pipeline; protected readonly MTLDevice _device; + protected readonly MetalRenderer _renderer; protected MTLTexture _mtlTexture; @@ -21,9 +22,10 @@ namespace Ryujinx.Graphics.Metal public int Height => Info.Height; public int Depth => Info.Depth; - public TextureBase(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) + public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) { _device = device; + _renderer = renderer; _pipeline = pipeline; _info = info; } diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs index 38468a76c..3db1e7c4a 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs @@ -2,33 +2,32 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System; using System.Buffers; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class TextureBuffer : Texture, ITexture + class TextureBuffer : ITexture { - private MTLBuffer? _bufferHandle; + private readonly MetalRenderer _renderer; + + private BufferHandle _bufferHandle; private int _offset; private int _size; - public TextureBuffer(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) : base(device, pipeline, info) { } + private int _bufferCount; - public void CreateView() + public int Width { get; } + public int Height { get; } + + public MTLPixelFormat MtlFormat { get; } + + public TextureBuffer(MetalRenderer renderer, TextureCreateInfo info) { - var descriptor = new MTLTextureDescriptor - { - PixelFormat = FormatTable.GetFormat(Info.Format), - Usage = MTLTextureUsage.ShaderRead | MTLTextureUsage.ShaderWrite, - StorageMode = MTLStorageMode.Shared, - TextureType = Info.Target.Convert(), - Width = (ulong)Info.Width, - Height = (ulong)Info.Height - }; - - _mtlTexture = _bufferHandle.Value.NewTexture(descriptor, (ulong)_offset, (ulong)_size); + _renderer = renderer; + Width = info.Width; + Height = info.Height; + MtlFormat = FormatTable.GetFormat(info.Format); } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) @@ -51,10 +50,9 @@ namespace Ryujinx.Graphics.Metal throw new NotSupportedException(); } - // TODO: Implement this method public PinnedSpan GetData() { - throw new NotImplementedException(); + return _renderer.GetBufferData(_bufferHandle, _offset, _size); } public PinnedSpan GetData(int layer, int level) @@ -67,10 +65,14 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } + public void Release() + { + + } + public void SetData(IMemoryOwner data) { - // TODO - //_gd.SetBufferData(_bufferHandle, _offset, data.Memory.Span); + _renderer.SetBufferData(_bufferHandle, _offset, data.Memory.Span); data.Dispose(); } @@ -86,25 +88,20 @@ namespace Ryujinx.Graphics.Metal public void SetStorage(BufferRange buffer) { - if (buffer.Handle != BufferHandle.Null) + if (_bufferHandle == buffer.Handle && + _offset == buffer.Offset && + _size == buffer.Size && + _bufferCount == _renderer.BufferManager.BufferCount) { - var handle = buffer.Handle; - MTLBuffer bufferHandle = new(Unsafe.As(ref handle)); - if (_bufferHandle == bufferHandle && - _offset == buffer.Offset && - _size == buffer.Size) - { - return; - } - - _bufferHandle = bufferHandle; - _offset = buffer.Offset; - _size = buffer.Size; - - Release(); - - CreateView(); + return; } + + _bufferHandle = buffer.Handle; + _offset = buffer.Offset; + _size = buffer.Size; + _bufferCount = _renderer.BufferManager.BufferCount; + + Release(); } } } -- 2.47.1 From 20cf1a08c136aaaf4655cef01da657dbec667824 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 19 Jun 2024 23:14:23 +0100 Subject: [PATCH 231/368] don't recreate render pipeline unless we're about to draw, pass view depth properly (#22) --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 8 ++++---- src/Ryujinx.Graphics.Metal/Pipeline.cs | 19 +++++++++++-------- src/Ryujinx.Graphics.Metal/Texture.cs | 7 +------ 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index bed199987..ea6c4e31b 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Metal fixed (float* ptr = region) { - _pipeline.GetOrCreateRenderEncoder().SetVertexBytes((IntPtr)ptr, RegionBufferSize, 0); + _pipeline.GetOrCreateRenderEncoder(true).SetVertexBytes((IntPtr)ptr, RegionBufferSize, 0); } _pipeline.Draw(4, 1, 0, 0); @@ -191,7 +191,7 @@ namespace Ryujinx.Graphics.Metal fixed (float* ptr = region) { - _pipeline.GetOrCreateRenderEncoder().SetVertexBytes((IntPtr)ptr, RegionBufferSize, 0); + _pipeline.GetOrCreateRenderEncoder(true).SetVertexBytes((IntPtr)ptr, RegionBufferSize, 0); } _pipeline.Draw(4, 1, 0, 0); @@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.Metal fixed (float* ptr = clearColor) { - _pipeline.GetOrCreateRenderEncoder().SetFragmentBytes((IntPtr)ptr, ClearColorBufferSize, 0); + _pipeline.GetOrCreateRenderEncoder(true).SetFragmentBytes((IntPtr)ptr, ClearColorBufferSize, 0); } _pipeline.Draw(4, 1, 0, 0); @@ -276,7 +276,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetViewports(viewports); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); // _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); - _pipeline.GetOrCreateRenderEncoder().SetFragmentBytes(ptr, ClearDepthBufferSize, 0); + _pipeline.GetOrCreateRenderEncoder(true).SetFragmentBytes(ptr, ClearDepthBufferSize, 0); _pipeline.Draw(4, 1, 0, 0); // Restore previous state diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 9e2e4f26f..1230bb120 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -76,7 +76,7 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager.SetClearLoadAction(clear); } - public MTLRenderCommandEncoder GetOrCreateRenderEncoder() + public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false) { MTLRenderCommandEncoder renderCommandEncoder; if (_currentEncoder == null || _currentEncoderType != EncoderType.Render) @@ -88,7 +88,10 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder = new MTLRenderCommandEncoder(_currentEncoder.Value); } - _encoderStateManager.RebindRenderState(renderCommandEncoder); + if (forDraw) + { + _encoderStateManager.RebindRenderState(renderCommandEncoder); + } return renderCommandEncoder; } @@ -325,7 +328,7 @@ namespace Ryujinx.Graphics.Metal public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + var renderCommandEncoder = GetOrCreateRenderEncoder(true); // TODO: Support topology re-indexing to provide support for TriangleFans var primitiveType = _encoderStateManager.Topology.Convert(); @@ -340,7 +343,7 @@ namespace Ryujinx.Graphics.Metal public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + var renderCommandEncoder = GetOrCreateRenderEncoder(true); // TODO: Support topology re-indexing to provide support for TriangleFans var primitiveType = _encoderStateManager.Topology.Convert(); @@ -360,28 +363,28 @@ namespace Ryujinx.Graphics.Metal public void DrawIndexedIndirect(BufferRange indirectBuffer) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(true); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(true); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndirect(BufferRange indirectBuffer) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(true); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(); + // var renderCommandEncoder = GetOrCreateRenderEncoder(true); Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 7d56375a6..da52e61e7 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -46,12 +46,7 @@ namespace Ryujinx.Graphics.Metal levels.length = (ulong)Info.Levels; NSRange slices; slices.location = (ulong)firstLayer; - slices.length = 1; - - if (info.Target != Target.Texture3D && info.Target != Target.Cubemap) - { - slices.length = (ulong)Info.Depth; - } + slices.length = (ulong)Info.Depth; var swizzle = GetSwizzle(info, pixelFormat); -- 2.47.1 From b8779c6e09f3128193fcccb80fc13bde61f50b4f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Thu, 20 Jun 2024 12:59:29 +0100 Subject: [PATCH 232/368] Buffer Conversions (#23) * Why is this not working * Revert helper shader changes for now * Byte Index Buffer Restride --- src/Ryujinx.Graphics.Metal/BufferHolder.cs | 59 ++++ src/Ryujinx.Graphics.Metal/BufferManager.cs | 10 + src/Ryujinx.Graphics.Metal/CacheByRange.cs | 333 ++++++++++++++++++ src/Ryujinx.Graphics.Metal/EncoderState.cs | 27 +- .../EncoderStateManager.cs | 164 +++++++-- src/Ryujinx.Graphics.Metal/HelperShader.cs | 72 +++- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 11 +- .../Ryujinx.Graphics.Metal.csproj | 1 + .../Shaders/ChangeBufferStride.metal | 52 +++ 10 files changed, 692 insertions(+), 39 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/CacheByRange.cs create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs index 7fe3530eb..c44c59eec 100644 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Metal/BufferHolder.cs @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public class BufferHolder : IDisposable { + private CacheByRange _cachedConvertedBuffers; + public int Size { get; } private readonly IntPtr _map; @@ -271,9 +273,66 @@ namespace Ryujinx.Graphics.Metal _waitable.WaitForFences(offset, size); } + private bool BoundToRange(int offset, ref int size) + { + if (offset >= Size) + { + return false; + } + + size = Math.Min(Size - offset, size); + + return true; + } + + public Auto GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size) + { + if (!BoundToRange(offset, ref size)) + { + return null; + } + + var key = new I8ToI16CacheKey(_renderer); + + if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) + { + holder = _renderer.BufferManager.Create((size * 2 + 3) & ~3); + + _renderer.HelperShader.ConvertI8ToI16(cbs, this, holder, offset, size); + + key.SetBuffer(holder.GetBuffer()); + + _cachedConvertedBuffers.Add(offset, size, key, holder); + } + + return holder.GetBuffer(); + } + + public bool TryGetCachedConvertedBuffer(int offset, int size, ICacheKey key, out BufferHolder holder) + { + return _cachedConvertedBuffers.TryGetValue(offset, size, key, out holder); + } + + public void AddCachedConvertedBuffer(int offset, int size, ICacheKey key, BufferHolder holder) + { + _cachedConvertedBuffers.Add(offset, size, key, holder); + } + + public void AddCachedConvertedBufferDependency(int offset, int size, ICacheKey key, Dependency dependency) + { + _cachedConvertedBuffers.AddDependency(offset, size, key, dependency); + } + + public void RemoveCachedConvertedBuffer(int offset, int size, ICacheKey key) + { + _cachedConvertedBuffers.Remove(offset, size, key); + } + + public void Dispose() { _buffer.Dispose(); + _cachedConvertedBuffers.Dispose(); _flushLock.EnterWriteLock(); diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs index 8f6c2daa7..bf7f00901 100644 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -153,6 +153,16 @@ namespace Ryujinx.Graphics.Metal return null; } + public Auto GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size) + { + if (TryGetBuffer(handle, out var holder)) + { + return holder.GetBufferI8ToI16(cbs, offset, size); + } + + return null; + } + public PinnedSpan GetData(BufferHandle handle, int offset, int size) { if (TryGetBuffer(handle, out var holder)) diff --git a/src/Ryujinx.Graphics.Metal/CacheByRange.cs b/src/Ryujinx.Graphics.Metal/CacheByRange.cs new file mode 100644 index 000000000..d507dcaeb --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/CacheByRange.cs @@ -0,0 +1,333 @@ +using SharpMetal.Metal; +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + public interface ICacheKey : IDisposable + { + bool KeyEqual(ICacheKey other); + } + + [SupportedOSPlatform("macos")] + struct I8ToI16CacheKey : ICacheKey + { + // Used to notify the pipeline that bindings have invalidated on dispose. + private readonly MetalRenderer _renderer; + private Auto _buffer; + + public I8ToI16CacheKey(MetalRenderer renderer) + { + _renderer = renderer; + _buffer = null; + } + + public readonly bool KeyEqual(ICacheKey other) + { + return other is I8ToI16CacheKey; + } + + public void SetBuffer(Auto buffer) + { + _buffer = buffer; + } + + public void Dispose() + { + // TODO: Tell pipeline buffer is dirty! + // _renderer.PipelineInternal.DirtyIndexBuffer(_buffer); + } + } + + [SupportedOSPlatform("macos")] + struct AlignedVertexBufferCacheKey : ICacheKey + { + private readonly int _stride; + private readonly int _alignment; + + // Used to notify the pipeline that bindings have invalidated on dispose. + private readonly MetalRenderer _renderer; + private Auto _buffer; + + public AlignedVertexBufferCacheKey(MetalRenderer renderer, int stride, int alignment) + { + _renderer = renderer; + _stride = stride; + _alignment = alignment; + _buffer = null; + } + + public readonly bool KeyEqual(ICacheKey other) + { + return other is AlignedVertexBufferCacheKey entry && + entry._stride == _stride && + entry._alignment == _alignment; + } + + public void SetBuffer(Auto buffer) + { + _buffer = buffer; + } + + public readonly void Dispose() + { + // TODO: Tell pipeline buffer is dirty! + // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); + } + } + + [SupportedOSPlatform("macos")] + struct TopologyConversionCacheKey : ICacheKey + { + // TODO: Patterns + // private readonly IndexBufferPattern _pattern; + private readonly int _indexSize; + + // Used to notify the pipeline that bindings have invalidated on dispose. + private readonly MetalRenderer _renderer; + private Auto _buffer; + + public TopologyConversionCacheKey(MetalRenderer renderer, /*IndexBufferPattern pattern, */int indexSize) + { + _renderer = renderer; + // _pattern = pattern; + _indexSize = indexSize; + _buffer = null; + } + + public readonly bool KeyEqual(ICacheKey other) + { + return other is TopologyConversionCacheKey entry && + // entry._pattern == _pattern && + entry._indexSize == _indexSize; + } + + public void SetBuffer(Auto buffer) + { + _buffer = buffer; + } + + public readonly void Dispose() + { + // TODO: Tell pipeline buffer is dirty! + // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); + } + } + + [SupportedOSPlatform("macos")] + public readonly struct Dependency + { + private readonly BufferHolder _buffer; + private readonly int _offset; + private readonly int _size; + private readonly ICacheKey _key; + + public Dependency(BufferHolder buffer, int offset, int size, ICacheKey key) + { + _buffer = buffer; + _offset = offset; + _size = size; + _key = key; + } + + public void RemoveFromOwner() + { + _buffer.RemoveCachedConvertedBuffer(_offset, _size, _key); + } + } + + [SupportedOSPlatform("macos")] + struct CacheByRange where T : IDisposable + { + private struct Entry + { + public ICacheKey Key; + public T Value; + public List DependencyList; + + public Entry(ICacheKey key, T value) + { + Key = key; + Value = value; + DependencyList = null; + } + + public readonly void InvalidateDependencies() + { + if (DependencyList != null) + { + foreach (Dependency dependency in DependencyList) + { + dependency.RemoveFromOwner(); + } + + DependencyList.Clear(); + } + } + } + + private Dictionary> _ranges; + + public void Add(int offset, int size, ICacheKey key, T value) + { + List entries = GetEntries(offset, size); + + entries.Add(new Entry(key, value)); + } + + public void AddDependency(int offset, int size, ICacheKey key, Dependency dependency) + { + List entries = GetEntries(offset, size); + + for (int i = 0; i < entries.Count; i++) + { + Entry entry = entries[i]; + + if (entry.Key.KeyEqual(key)) + { + if (entry.DependencyList == null) + { + entry.DependencyList = new List(); + entries[i] = entry; + } + + entry.DependencyList.Add(dependency); + + break; + } + } + } + + public void Remove(int offset, int size, ICacheKey key) + { + List entries = GetEntries(offset, size); + + for (int i = 0; i < entries.Count; i++) + { + Entry entry = entries[i]; + + if (entry.Key.KeyEqual(key)) + { + entries.RemoveAt(i--); + + DestroyEntry(entry); + } + } + + if (entries.Count == 0) + { + _ranges.Remove(PackRange(offset, size)); + } + } + + public bool TryGetValue(int offset, int size, ICacheKey key, out T value) + { + List entries = GetEntries(offset, size); + + foreach (Entry entry in entries) + { + if (entry.Key.KeyEqual(key)) + { + value = entry.Value; + + return true; + } + } + + value = default; + return false; + } + + public void Clear() + { + if (_ranges != null) + { + foreach (List entries in _ranges.Values) + { + foreach (Entry entry in entries) + { + DestroyEntry(entry); + } + } + + _ranges.Clear(); + _ranges = null; + } + } + + public readonly void ClearRange(int offset, int size) + { + if (_ranges != null && _ranges.Count > 0) + { + int end = offset + size; + + List toRemove = null; + + foreach (KeyValuePair> range in _ranges) + { + (int rOffset, int rSize) = UnpackRange(range.Key); + + int rEnd = rOffset + rSize; + + if (rEnd > offset && rOffset < end) + { + List entries = range.Value; + + foreach (Entry entry in entries) + { + DestroyEntry(entry); + } + + (toRemove ??= new List()).Add(range.Key); + } + } + + if (toRemove != null) + { + foreach (ulong range in toRemove) + { + _ranges.Remove(range); + } + } + } + } + + private List GetEntries(int offset, int size) + { + _ranges ??= new Dictionary>(); + + ulong key = PackRange(offset, size); + + if (!_ranges.TryGetValue(key, out List value)) + { + value = new List(); + _ranges.Add(key, value); + } + + return value; + } + + private static void DestroyEntry(Entry entry) + { + entry.Key.Dispose(); + entry.Value?.Dispose(); + entry.InvalidateDependencies(); + } + + private static ulong PackRange(int offset, int size) + { + return (uint)offset | ((ulong)size << 32); + } + + private static (int offset, int size) UnpackRange(ulong range) + { + return ((int)range, (int)(range >> 32)); + } + + public void Dispose() + { + Clear(); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index d0d963ae1..811c68995 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System.Linq; @@ -21,6 +22,26 @@ namespace Ryujinx.Graphics.Metal } } + public record struct BufferRef + { + public Auto Buffer; + public int Index; + public BufferRange? Range; + + public BufferRef(Auto buffer, int index) + { + Buffer = buffer; + Index = index; + } + + public BufferRef(Auto buffer, int index, ref BufferRange range) + { + Buffer = buffer; + Index = index; + Range = range; + } + } + [SupportedOSPlatform("macos")] struct EncoderState { @@ -37,10 +58,10 @@ namespace Ryujinx.Graphics.Metal public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTextures]; public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxSamplers]; - public BufferAssignment[] UniformBuffers = []; - public BufferAssignment[] StorageBuffers = []; + public BufferRef[] UniformBuffers = []; + public BufferRef[] StorageBuffers = []; - public BufferRange IndexBuffer = default; + public Auto IndexBuffer = default; public MTLIndexType IndexType = MTLIndexType.UInt16; public ulong IndexBufferOffset = 0; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 641d1e2ac..e6933eeb2 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Metal private EncoderState _currentState = new(); private readonly Stack _backStates = []; - public readonly BufferRange IndexBuffer => _currentState.IndexBuffer; + public readonly Auto IndexBuffer => _currentState.IndexBuffer; public readonly MTLIndexType IndexType => _currentState.IndexType; public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; public readonly PrimitiveTopology Topology => _currentState.Topology; @@ -356,9 +356,18 @@ namespace Ryujinx.Graphics.Metal { if (buffer.Handle != BufferHandle.Null) { - _currentState.IndexType = type.Convert(); - _currentState.IndexBufferOffset = (ulong)buffer.Offset; - _currentState.IndexBuffer = buffer; + if (type == GAL.IndexType.UByte) + { + _currentState.IndexType = MTLIndexType.UInt16; + _currentState.IndexBufferOffset = (ulong)buffer.Offset; + _currentState.IndexBuffer = _bufferManager.GetBufferI8ToI16(_pipeline.CurrentCommandBuffer, buffer.Handle, buffer.Offset, buffer.Size); + } + else + { + _currentState.IndexType = type.Convert(); + _currentState.IndexBufferOffset = (ulong)buffer.Offset; + _currentState.IndexBuffer = _bufferManager.GetBuffer(buffer.Handle, false); + } } } @@ -659,7 +668,20 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateUniformBuffers(ReadOnlySpan buffers) { - _currentState.UniformBuffers = buffers.ToArray(); + _currentState.UniformBuffers = new BufferRef[buffers.Length]; + + for (int i = 0; i < buffers.Length; i++) + { + var assignment = buffers[i]; + var buffer = assignment.Range; + int index = assignment.Binding; + + Auto mtlBuffer = buffer.Handle == BufferHandle.Null + ? null + : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); + + _currentState.UniformBuffers[i] = new BufferRef(mtlBuffer, index, ref buffer); + } // Inline update if (_pipeline.CurrentEncoder != null) @@ -680,13 +702,49 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateStorageBuffers(ReadOnlySpan buffers) { - _currentState.StorageBuffers = buffers.ToArray(); + _currentState.StorageBuffers = new BufferRef[buffers.Length]; - for (int i = 0; i < _currentState.StorageBuffers.Length; i++) + for (int i = 0; i < buffers.Length; i++) { - BufferAssignment buffer = _currentState.StorageBuffers[i]; - // TODO: DONT offset the binding by 15 - _currentState.StorageBuffers[i] = new BufferAssignment(buffer.Binding + 15, buffer.Range); + var assignment = buffers[i]; + var buffer = assignment.Range; + // TODO: Dont do this + int index = assignment.Binding + 15; + + Auto mtlBuffer = buffer.Handle == BufferHandle.Null + ? null + : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); + + _currentState.StorageBuffers[i] = new BufferRef(mtlBuffer, index, ref buffer); + } + + // Inline update + if (_pipeline.CurrentEncoder != null) + { + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + } + else if (_pipeline.CurrentEncoderType == EncoderType.Compute) + { + var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); + SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); + } + } + } + + // Inlineable + public void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) + { + _currentState.StorageBuffers = new BufferRef[buffers.Length]; + + for (int i = 0; i < buffers.Length; i++) + { + var mtlBuffer = buffers[i]; + int index = first + i; + + _currentState.StorageBuffers[i] = new BufferRef(mtlBuffer, index); } // Inline update @@ -938,51 +996,95 @@ namespace Ryujinx.Graphics.Metal private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) { - var buffers = new List(); + var buffers = new List(); for (int i = 0; i < bufferDescriptors.Length; i++) { - buffers.Add(new BufferAssignment(i, bufferDescriptors[i].Buffer)); + Auto mtlBuffer = bufferDescriptors[i].Buffer.Handle == BufferHandle.Null + ? null + : _bufferManager.GetBuffer(bufferDescriptors[i].Buffer.Handle, bufferDescriptors[i].Buffer.Write); + + var range = bufferDescriptors[i].Buffer; + + buffers.Add(new BufferRef(mtlBuffer, i, ref range)); } + var zeroBufferRange = new BufferRange(_zeroBuffer, 0, ZeroBufferSize); + + Auto zeroBuffer = _zeroBuffer == BufferHandle.Null + ? null + : _bufferManager.GetBuffer(_zeroBuffer, false); + // Zero buffer - buffers.Add(new BufferAssignment( - bufferDescriptors.Length, - new BufferRange(_zeroBuffer, 0, ZeroBufferSize))); + buffers.Add(new BufferRef(zeroBuffer, bufferDescriptors.Length, ref zeroBufferRange)); SetRenderBuffers(renderCommandEncoder, buffers.ToArray()); } - private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferAssignment[] buffers, bool fragment = false) + private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] buffers, bool fragment = false) { - foreach (var buffer in buffers) + for (int i = 0; i < buffers.Length; i++) { - var range = buffer.Range; - var autoBuffer = _bufferManager.GetBuffer(range.Handle, range.Offset, range.Size, range.Write); + var range = buffers[i].Range; + var autoBuffer = buffers[i].Buffer; + var offset = 0; + var index = buffers[i].Index; - if (autoBuffer != null) + if (autoBuffer == null) { - var mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer).Value; + continue; + } - renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)range.Offset, (ulong)buffer.Binding); + MTLBuffer mtlBuffer; - if (fragment) - { - renderCommandEncoder.SetFragmentBuffer(mtlBuffer, (ulong)range.Offset, (ulong)buffer.Binding); - } + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer).Value; + } + + renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)index); + + if (fragment) + { + renderCommandEncoder.SetFragmentBuffer(mtlBuffer, (ulong)offset, (ulong)index); } } } - private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferAssignment[] buffers) + private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] buffers) { - foreach (var buffer in buffers) + for (int i = 0; i < buffers.Length; i++) { - var range = buffer.Range; - var mtlBuffer = _bufferManager.GetBuffer(range.Handle, range.Offset, range.Size, range.Write).Get(_pipeline.CurrentCommandBuffer).Value; + var range = buffers[i].Range; + var autoBuffer = buffers[i].Buffer; + var offset = 0; + var index = buffers[i].Index; - computeCommandEncoder.SetBuffer(mtlBuffer, (ulong)range.Offset, (ulong)buffer.Binding); + if (autoBuffer == null) + { + continue; + } + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer).Value; + } + + computeCommandEncoder.SetBuffer(mtlBuffer, (ulong)offset, (ulong)index); } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index ea6c4e31b..9b9d5d73e 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -12,7 +12,9 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public class HelperShader : IDisposable { + private const int ConvertElementsPerWorkgroup = 32 * 100; // Work group size of 32 times 100 elements. private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders"; + private readonly MetalRenderer _renderer; private readonly Pipeline _pipeline; private MTLDevice _device; @@ -21,10 +23,12 @@ namespace Ryujinx.Graphics.Metal private readonly IProgram _programColorBlit; private readonly List _programsColorClear = new(); private readonly IProgram _programDepthStencilClear; + private readonly IProgram _programStrideChange; - public HelperShader(MTLDevice device, Pipeline pipeline) + public HelperShader(MTLDevice device, MetalRenderer renderer, Pipeline pipeline) { _device = device; + _renderer = renderer; _pipeline = pipeline; _samplerNearest = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); @@ -54,6 +58,12 @@ namespace Ryujinx.Graphics.Metal new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) ], device); + + var strideChangeSource = ReadMsl("ChangeBufferStride.metal"); + _programStrideChange = new Program( + [ + new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) + ], device); } private static string ReadMsl(string fileName) @@ -62,6 +72,7 @@ namespace Ryujinx.Graphics.Metal } public unsafe void BlitColor( + CommandBufferScoped cbs, ITexture src, ITexture dst, Extents2D srcRegion, @@ -89,6 +100,10 @@ namespace Ryujinx.Graphics.Metal (region[2], region[3]) = (region[3], region[2]); } + // using var buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize); + // buffer.Holder.SetDataUnchecked(buffer.Offset, region); + // _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); + var rect = new Rectangle( MathF.Min(dstRegion.X1, dstRegion.X2), MathF.Min(dstRegion.Y1, dstRegion.Y2), @@ -156,6 +171,10 @@ namespace Ryujinx.Graphics.Metal (region[2], region[3]) = (region[3], region[2]); } + // var bufferHandle = _renderer.BufferManager.CreateWithHandle(RegionBufferSize); + // _renderer.BufferManager.SetData(bufferHandle, 0, region); + // _pipeline.SetUniformBuffers([new BufferAssignment(0, new BufferRange(bufferHandle, 0, RegionBufferSize))]); + Span viewports = stackalloc Viewport[1]; Span> scissors = stackalloc Rectangle[1]; @@ -200,6 +219,57 @@ namespace Ryujinx.Graphics.Metal _pipeline.RestoreState(); } + public void ConvertI8ToI16(CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size) + { + ChangeStride(cbs, src, dst, srcOffset, size, 1, 2); + } + + public unsafe void ChangeStride( + CommandBufferScoped cbs, + BufferHolder src, + BufferHolder dst, + int srcOffset, + int size, + int stride, + int newStride) + { + int elems = size / stride; + + var srcBuffer = src.GetBuffer(); + var dstBuffer = dst.GetBuffer(); + + const int ParamsBufferSize = 16; + + // Save current state + _pipeline.SaveAndResetState(); + + Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; + + shaderParams[0] = stride; + shaderParams[1] = newStride; + shaderParams[2] = size; + shaderParams[3] = srcOffset; + + using var buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); + + buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); + + _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); + + Span> sbRanges = new Auto[2]; + + sbRanges[0] = srcBuffer; + sbRanges[1] = dstBuffer; + + _pipeline.SetStorageBuffers(1, sbRanges); + + _pipeline.SetProgram(_programStrideChange); + _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 64, 1, 1); + + // Restore previous state + _pipeline.RestoreState(); + } + public unsafe void ClearColor( int index, ReadOnlySpan clearColor, diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 1f61090f0..aac88587d 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.InitEncoderStateManager(_bufferManager); - _helperShader = new HelperShader(_device, _pipeline); + _helperShader = new HelperShader(_device, this, _pipeline); SyncManager = new SyncManager(this); } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 1230bb120..7a83a02a7 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -193,7 +193,7 @@ namespace Ryujinx.Graphics.Metal var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); var dst = new Texture(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0); - _renderer.HelperShader.BlitColor(src, dst, srcRegion, dstRegion, isLinear); + _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear); EndCurrentPass(); @@ -227,7 +227,7 @@ namespace Ryujinx.Graphics.Metal Extents2D dstRegion, bool linearFilter) { - _renderer.HelperShader.BlitColor(src, dst, srcRegion, dstRegion, linearFilter); + _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); } public void Barrier() @@ -348,7 +348,7 @@ namespace Ryujinx.Graphics.Metal // TODO: Support topology re-indexing to provide support for TriangleFans var primitiveType = _encoderStateManager.Topology.Convert(); - var indexBuffer = _renderer.BufferManager.GetBuffer(_encoderStateManager.IndexBuffer.Handle, false); + var indexBuffer = _encoderStateManager.IndexBuffer; renderCommandEncoder.DrawIndexedPrimitives( primitiveType, @@ -546,6 +546,11 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager.UpdateStorageBuffers(buffers); } + public void SetStorageBuffers(int first, ReadOnlySpan> buffers) + { + _encoderStateManager.UpdateStorageBuffers(first, buffers); + } + public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) { if (texture is TextureBase tex) diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 0824accc1..f4e98cd45 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal new file mode 100644 index 000000000..64e832092 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal @@ -0,0 +1,52 @@ +#include + +using namespace metal; + +kernel void kernelMain(constant int4& stride_arguments [[buffer(0)]], + device uint8_t* in_data [[buffer(1)]], + device uint8_t* out_data [[buffer(2)]], + uint3 thread_position_in_grid [[thread_position_in_grid]], + uint3 threads_per_threadgroup [[threads_per_threadgroup]], + uint3 threadgroups_per_grid [[threads_per_grid]]) +{ + // Determine what slice of the stride copies this invocation will perform. + + int sourceStride = stride_arguments.x; + int targetStride = stride_arguments.y; + int bufferSize = stride_arguments.z; + int sourceOffset = stride_arguments.w; + + int strideRemainder = targetStride - sourceStride; + int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); + + int copiesRequired = bufferSize / sourceStride; + + // Find the copies that this invocation should perform. + + // - Copies that all invocations perform. + int allInvocationCopies = copiesRequired / invocations; + + // - Extra remainder copy that this invocation performs. + int index = int(thread_position_in_grid.x); + int extra = (index < (copiesRequired % invocations)) ? 1 : 0; + + int copyCount = allInvocationCopies + extra; + + // Finally, get the starting offset. Make sure to count extra copies. + + int startCopy = allInvocationCopies * index + min(copiesRequired % invocations, index); + + int srcOffset = sourceOffset + startCopy * sourceStride; + int dstOffset = startCopy * targetStride; + + // Perform the copies for this region + for (int i = 0; i < copyCount; i++) { + for (int j = 0; j < sourceStride; j++) { + out_data[dstOffset++] = in_data[srcOffset++]; + } + + for (int j = 0; j < strideRemainder; j++) { + out_data[dstOffset++] = uint8_t(0); + } + } +} -- 2.47.1 From c3e39a9c9139b4b3bb8e8c3f6ed60dc84e6f1604 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 14:14:05 +0100 Subject: [PATCH 233/368] Use buffer manager for color blit --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 30 ++++++++++------------ 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 9b9d5d73e..f83675698 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -79,10 +79,15 @@ namespace Ryujinx.Graphics.Metal Extents2D dstRegion, bool linearFilter) { + // Save current state + _pipeline.SaveAndResetState(); + const int RegionBufferSize = 16; var sampler = linearFilter ? _samplerLinear : _samplerNearest; + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); + Span region = stackalloc float[RegionBufferSize / sizeof(float)]; region[0] = srcRegion.X1 / (float)src.Width; @@ -100,9 +105,9 @@ namespace Ryujinx.Graphics.Metal (region[2], region[3]) = (region[3], region[2]); } - // using var buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize); - // buffer.Holder.SetDataUnchecked(buffer.Offset, region); - // _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); + using var buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize); + buffer.Holder.SetDataUnchecked(buffer.Offset, region); + _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); var rect = new Rectangle( MathF.Min(dstRegion.X1, dstRegion.X2), @@ -121,25 +126,18 @@ namespace Ryujinx.Graphics.Metal 0f, 1f); + _pipeline.SetProgram(_programColorBlit); + int dstWidth = dst.Width; int dstHeight = dst.Height; - // Save current state - _pipeline.SaveAndResetState(); - - _pipeline.SetProgram(_programColorBlit); - _pipeline.SetViewports(viewports); - _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dstWidth, dstHeight) }); _pipeline.SetRenderTargets([dst], null); + _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dstWidth, dstHeight) }); + _pipeline.SetClearLoadAction(true); - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); + + _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - - fixed (float* ptr = region) - { - _pipeline.GetOrCreateRenderEncoder(true).SetVertexBytes((IntPtr)ptr, RegionBufferSize, 0); - } - _pipeline.Draw(4, 1, 0, 0); // Restore previous state -- 2.47.1 From bf4232a35b0be3de01191e30a333d1b2f10aa79b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 14:25:40 +0100 Subject: [PATCH 234/368] Helper shader cleanup --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 56 ++++++++-------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index f83675698..44e34aceb 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -150,8 +150,13 @@ namespace Ryujinx.Graphics.Metal Extents2DF srcRegion, Extents2DF dstRegion) { + // Save current state + _pipeline.SaveState(); + const int RegionBufferSize = 16; + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler); + Span region = stackalloc float[RegionBufferSize / sizeof(float)]; region[0] = srcRegion.X1 / src.Width; @@ -169,12 +174,11 @@ namespace Ryujinx.Graphics.Metal (region[2], region[3]) = (region[3], region[2]); } - // var bufferHandle = _renderer.BufferManager.CreateWithHandle(RegionBufferSize); - // _renderer.BufferManager.SetData(bufferHandle, 0, region); - // _pipeline.SetUniformBuffers([new BufferAssignment(0, new BufferRange(bufferHandle, 0, RegionBufferSize))]); + var bufferHandle = _renderer.BufferManager.CreateWithHandle(RegionBufferSize); + _renderer.BufferManager.SetData(bufferHandle, 0, region); + _pipeline.SetUniformBuffers([new BufferAssignment(0, new BufferRange(bufferHandle, 0, RegionBufferSize))]); Span viewports = stackalloc Viewport[1]; - Span> scissors = stackalloc Rectangle[1]; var rect = new Rectangle( MathF.Min(dstRegion.X1, dstRegion.X2), @@ -191,28 +195,13 @@ namespace Ryujinx.Graphics.Metal 0f, 1f); - scissors[0] = new Rectangle(0, 0, 0xFFFF, 0xFFFF); - - // Save current state - _pipeline.SaveState(); - _pipeline.SetProgram(_programColorBlit); _pipeline.SetViewports(viewports); - _pipeline.SetScissors(scissors); - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.SetFaceCulling(false, Face.FrontAndBack); - // For some reason this results in a SIGSEGV - // _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - - fixed (float* ptr = region) - { - _pipeline.GetOrCreateRenderEncoder(true).SetVertexBytes((IntPtr)ptr, RegionBufferSize, 0); - } - _pipeline.Draw(4, 1, 0, 0); + _renderer.BufferManager.Delete(bufferHandle); + // Restore previous state _pipeline.RestoreState(); } @@ -249,16 +238,13 @@ namespace Ryujinx.Graphics.Metal shaderParams[3] = srcOffset; using var buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); Span> sbRanges = new Auto[2]; sbRanges[0] = srcBuffer; sbRanges[1] = dstBuffer; - _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programStrideChange); @@ -275,11 +261,17 @@ namespace Ryujinx.Graphics.Metal int dstWidth, int dstHeight) { - const int ClearColorBufferSize = 16; - // Save current state _pipeline.SaveState(); + const int ClearColorBufferSize = 16; + + // TODO: Flush + + using var buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.CurrentCommandBuffer, ClearColorBufferSize); + buffer.Holder.SetDataUnchecked(buffer.Offset, clearColor); + _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); + Span viewports = stackalloc Viewport[1]; // TODO: Set exact viewport! @@ -293,18 +285,9 @@ namespace Ryujinx.Graphics.Metal 1f); _pipeline.SetProgram(_programsColorClear[index]); - _pipeline.SetBlendState(index, new BlendDescriptor(false, new ColorF(0f, 0f, 0f, 1f), BlendOp.Add, BlendFactor.One, BlendFactor.Zero, BlendOp.Add, BlendFactor.One, BlendFactor.Zero)); - _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); _pipeline.SetRenderTargetColorMasks([componentMask]); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetViewports(viewports); - - fixed (float* ptr = clearColor) - { - _pipeline.GetOrCreateRenderEncoder(true).SetFragmentBytes((IntPtr)ptr, ClearColorBufferSize, 0); - } - + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); // Restore previous state @@ -343,6 +326,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetViewports(viewports); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); + // TODO: Figure out why this causes a crash // _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); _pipeline.GetOrCreateRenderEncoder(true).SetFragmentBytes(ptr, ClearDepthBufferSize, 0); _pipeline.Draw(4, 1, 0, 0); -- 2.47.1 From 98ae46ba7035588e8dfd1f37bd616de2d4479bc7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 14:29:45 +0100 Subject: [PATCH 235/368] FIx regression --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 44e34aceb..b907173b6 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -285,6 +285,9 @@ namespace Ryujinx.Graphics.Metal 1f); _pipeline.SetProgram(_programsColorClear[index]); + _pipeline.SetBlendState(index, new BlendDescriptor(false, new ColorF(0f, 0f, 0f, 1f), BlendOp.Add, BlendFactor.One, BlendFactor.Zero, BlendOp.Add, BlendFactor.One, BlendFactor.Zero)); + _pipeline.SetFaceCulling(false, Face.Front); + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); _pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); -- 2.47.1 From 3451fbbbadb23ca36e372ff7a01f325a3cc9b692 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 19:11:12 +0100 Subject: [PATCH 236/368] Clear cached converted buffers on signaled write --- src/Ryujinx.Graphics.Metal/BufferHolder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs index c44c59eec..075ac21fe 100644 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Metal/BufferHolder.cs @@ -67,11 +67,11 @@ namespace Ryujinx.Graphics.Metal { if (offset == 0 && size == Size) { - // TODO: Cache converted buffers + _cachedConvertedBuffers.Clear(); } else { - // TODO: Cache converted buffers + _cachedConvertedBuffers.ClearRange(offset, size); } } -- 2.47.1 From c911db830910168d461ed77597a7a314cb7fb2e8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 21:08:28 +0100 Subject: [PATCH 237/368] Fix FEZ not showing anything Does not fix the underlying shortcomings of the cache system --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 1 - src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 4 ---- src/Ryujinx.Graphics.Metal/HelperShader.cs | 3 +-- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 811c68995..f99f52aec 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System.Linq; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e6933eeb2..9919db5f2 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -485,10 +485,6 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateStencilState(StencilTestDescriptor stencilTest) { - // Cleanup old state - _currentState.FrontFaceStencil.Dispose(); - _currentState.BackFaceStencil.Dispose(); - _currentState.FrontFaceStencil = new MTLStencilDescriptor { StencilFailureOperation = stencilTest.FrontSFail.Convert(), diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index b907173b6..0673a42c2 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -329,8 +329,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetViewports(viewports); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); - // TODO: Figure out why this causes a crash - // _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); + _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); _pipeline.GetOrCreateRenderEncoder(true).SetFragmentBytes(ptr, ClearDepthBufferSize, 0); _pipeline.Draw(4, 1, 0, 0); -- 2.47.1 From 175cded85d7d32337112908954a148956d766985 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 22:55:33 +0100 Subject: [PATCH 238/368] Match S8UintD24Unorm to Depth24UnormStencil8 Kind of works for es2gears --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 529758e78..f7f5eed46 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Metal Add(Format.R32G32B32A32Sint, MTLPixelFormat.RGBA32Sint); Add(Format.S8Uint, MTLPixelFormat.Stencil8); Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm); - // Add(Format.S8UintD24Unorm, MTLPixelFormat.BGRA8Unorm); + Add(Format.S8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8); Add(Format.D32Float, MTLPixelFormat.Depth32Float); Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24UnormStencil8); Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8); -- 2.47.1 From 74083083cde8c40f7d543b8dd97d93e99ba12aea Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 00:15:14 +0100 Subject: [PATCH 239/368] PersistentFlushBuffer + BackgroundResources --- .../BackgroundResources.cs | 110 ++++++++++++++++++ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 41 ++++--- .../PersistentFlushBuffer.cs | 66 +++++++++++ 3 files changed, 199 insertions(+), 18 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/BackgroundResources.cs create mode 100644 src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs diff --git a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs new file mode 100644 index 000000000..2f846392e --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs @@ -0,0 +1,110 @@ +using SharpMetal.Metal; +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; +using System.Threading; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + class BackgroundResource : IDisposable + { + private readonly MetalRenderer _renderer; + private readonly Pipeline _pipeline; + + private CommandBufferPool _pool; + private PersistentFlushBuffer _flushBuffer; + + public BackgroundResource(MetalRenderer renderer, Pipeline pipeline) + { + _renderer = renderer; + _pipeline = pipeline; + } + + public CommandBufferPool GetPool() + { + if (_pool == null) + { + MTLCommandQueue queue = _renderer.BackgroundQueue; + _pool = new CommandBufferPool(queue.Device, queue); + } + + return _pool; + } + + public PersistentFlushBuffer GetFlushBuffer() + { + _flushBuffer ??= new PersistentFlushBuffer(_renderer, _pipeline); + + return _flushBuffer; + } + + public void Dispose() + { + _pool?.Dispose(); + _flushBuffer?.Dispose(); + } + } + + [SupportedOSPlatform("macos")] + class BackgroundResources : IDisposable + { + private readonly MetalRenderer _renderer; + private readonly Pipeline _pipeline; + + private readonly Dictionary _resources; + + public BackgroundResources(MetalRenderer renderer, Pipeline pipeline) + { + _renderer = renderer; + _pipeline = pipeline; + + _resources = new Dictionary(); + } + + private void Cleanup() + { + lock (_resources) + { + foreach (KeyValuePair tuple in _resources) + { + if (!tuple.Key.IsAlive) + { + tuple.Value.Dispose(); + _resources.Remove(tuple.Key); + } + } + } + } + + public BackgroundResource Get() + { + Thread thread = Thread.CurrentThread; + + lock (_resources) + { + if (!_resources.TryGetValue(thread, out BackgroundResource resource)) + { + Cleanup(); + + resource = new BackgroundResource(_renderer, _pipeline); + + _resources[thread] = resource; + } + + return resource; + } + } + + public void Dispose() + { + lock (_resources) + { + foreach (var resource in _resources.Values) + { + resource.Dispose(); + } + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index aac88587d..08d1ca540 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -17,20 +17,21 @@ namespace Ryujinx.Graphics.Metal private readonly Func _getMetalLayer; private Pipeline _pipeline; - private HelperShader _helperShader; - private BufferManager _bufferManager; private Window _window; - private CommandBufferPool _commandBufferPool; public event EventHandler ScreenCaptured; public bool PreferThreading => true; + public IPipeline Pipeline => _pipeline; public IWindow Window => _window; - public HelperShader HelperShader => _helperShader; - public BufferManager BufferManager => _bufferManager; - public CommandBufferPool CommandBufferPool => _commandBufferPool; - public Action InterruptAction { get; private set; } - public SyncManager SyncManager { get; private set; } + + internal MTLCommandQueue BackgroundQueue { get; private set; } + internal HelperShader HelperShader { get; private set; } + internal BufferManager BufferManager { get; private set; } + internal CommandBufferPool CommandBufferPool { get; private set; } + internal BackgroundResources BackgroundResources { get; private set; } + internal Action InterruptAction { get; private set; } + internal SyncManager SyncManager { get; private set; } public MetalRenderer(Func metalLayer) { @@ -42,6 +43,8 @@ namespace Ryujinx.Graphics.Metal } _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers); + BackgroundQueue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers); + _getMetalLayer = metalLayer; } @@ -51,14 +54,15 @@ namespace Ryujinx.Graphics.Metal layer.Device = _device; layer.FramebufferOnly = false; - _commandBufferPool = new CommandBufferPool(_device, _queue); + CommandBufferPool = new CommandBufferPool(_device, _queue); _window = new Window(this, layer); _pipeline = new Pipeline(_device, this, _queue); - _bufferManager = new BufferManager(_device, this, _pipeline); + BufferManager = new BufferManager(_device, this, _pipeline); - _pipeline.InitEncoderStateManager(_bufferManager); + _pipeline.InitEncoderStateManager(BufferManager); - _helperShader = new HelperShader(_device, this, _pipeline); + BackgroundResources = new BackgroundResources(this, _pipeline); + HelperShader = new HelperShader(_device, this, _pipeline); SyncManager = new SyncManager(this); } @@ -69,12 +73,12 @@ namespace Ryujinx.Graphics.Metal public BufferHandle CreateBuffer(int size, BufferAccess access) { - return _bufferManager.CreateWithHandle(size); + return BufferManager.CreateWithHandle(size); } public BufferHandle CreateBuffer(IntPtr pointer, int size) { - return _bufferManager.Create(pointer, size); + return BufferManager.Create(pointer, size); } public BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers) @@ -125,12 +129,12 @@ namespace Ryujinx.Graphics.Metal public void DeleteBuffer(BufferHandle buffer) { - _bufferManager.Delete(buffer); + BufferManager.Delete(buffer); } public PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) { - return _bufferManager.GetData(buffer, offset, size); + return BufferManager.GetData(buffer, offset, size); } public Capabilities GetCapabilities() @@ -218,7 +222,7 @@ namespace Ryujinx.Graphics.Metal public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) { - _bufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPassDelegate); + BufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPassDelegate); } public void UpdateCounters() @@ -259,7 +263,7 @@ namespace Ryujinx.Graphics.Metal SyncManager.RegisterFlush(); // Periodically free unused regions of the staging buffer to avoid doing it all at once. - _bufferManager.StagingBuffer.FreeCompleted(); + BufferManager.StagingBuffer.FreeCompleted(); } public void SetInterruptAction(Action interruptAction) @@ -274,6 +278,7 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { + BackgroundResources.Dispose(); _pipeline.Dispose(); _window.Dispose(); } diff --git a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs b/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs new file mode 100644 index 000000000..6b51d4af5 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal class PersistentFlushBuffer : IDisposable + { + private readonly MetalRenderer _renderer; + private readonly Pipeline _pipeline; + + private BufferHolder _flushStorage; + + public PersistentFlushBuffer(MetalRenderer renderer, Pipeline pipeline) + { + _renderer = renderer; + _pipeline = pipeline; + } + + private BufferHolder ResizeIfNeeded(int size) + { + var flushStorage = _flushStorage; + + if (flushStorage == null || size > _flushStorage.Size) + { + flushStorage?.Dispose(); + + flushStorage = _renderer.BufferManager.Create(size); + _flushStorage = flushStorage; + } + + return flushStorage; + } + + public Span GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size) + { + var flushStorage = ResizeIfNeeded(size); + Auto srcBuffer; + + using (var cbs = cbp.Rent()) + { + srcBuffer = buffer.GetBuffer(); + var dstBuffer = flushStorage.GetBuffer(); + + if (srcBuffer.TryIncrementReferenceCount()) + { + BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false); + } + else + { + // Source buffer is no longer alive, don't copy anything to flush storage. + srcBuffer = null; + } + } + + flushStorage.WaitForFences(); + srcBuffer?.DecrementReferenceCount(); + return flushStorage.GetDataStorage(0, size); + } + + public void Dispose() + { + _flushStorage.Dispose(); + } + } +} -- 2.47.1 From 197184657f3d9bd0484b045a0975d74ae427cfaa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 00:21:06 +0100 Subject: [PATCH 240/368] Cleanup Pipeline Housekeeping More housekeeping --- src/Ryujinx.Graphics.Metal/Auto.cs | 6 +- .../BackgroundResources.cs | 2 +- src/Ryujinx.Graphics.Metal/BufferHolder.cs | 2 +- src/Ryujinx.Graphics.Metal/BufferManager.cs | 4 +- src/Ryujinx.Graphics.Metal/CacheByRange.cs | 5 +- .../CommandBufferPool.cs | 7 +- .../CommandBufferScoped.cs | 2 +- .../ComputePipelineCache.cs | 2 +- .../DepthStencilCache.cs | 4 +- .../DisposableBuffer.cs | 2 +- src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 +- .../EncoderStateManager.cs | 10 +-- src/Ryujinx.Graphics.Metal/FenceHolder.cs | 2 +- src/Ryujinx.Graphics.Metal/HelperShader.cs | 4 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 7 +- .../MultiFenceHolder.cs | 2 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 84 ++++++++----------- .../RenderPipelineCache.cs | 4 +- src/Ryujinx.Graphics.Metal/StagingBuffer.cs | 4 +- src/Ryujinx.Graphics.Metal/StateCache.cs | 2 +- src/Ryujinx.Graphics.Metal/StringHelper.cs | 2 +- src/Ryujinx.Graphics.Metal/SyncManager.cs | 2 +- src/Ryujinx.Graphics.Metal/Texture.cs | 4 +- src/Ryujinx.Graphics.Metal/TextureBase.cs | 2 +- 24 files changed, 77 insertions(+), 92 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Auto.cs b/src/Ryujinx.Graphics.Metal/Auto.cs index 8793923a7..7e79ecbc3 100644 --- a/src/Ryujinx.Graphics.Metal/Auto.cs +++ b/src/Ryujinx.Graphics.Metal/Auto.cs @@ -5,7 +5,7 @@ using System.Threading; namespace Ryujinx.Graphics.Metal { - public interface IAuto + interface IAuto { bool HasCommandBufferDependency(CommandBufferScoped cbs); @@ -14,13 +14,13 @@ namespace Ryujinx.Graphics.Metal void DecrementReferenceCount(); } - public interface IAutoPrivate : IAuto + interface IAutoPrivate : IAuto { void AddCommandBufferDependencies(CommandBufferScoped cbs); } [SupportedOSPlatform("macos")] - public class Auto : IAutoPrivate, IDisposable where T : IDisposable + class Auto : IAutoPrivate, IDisposable where T : IDisposable { private int _referenceCount; private T _value; diff --git a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs index 2f846392e..f02fd7205 100644 --- a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs +++ b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Metal if (_pool == null) { MTLCommandQueue queue = _renderer.BackgroundQueue; - _pool = new CommandBufferPool(queue.Device, queue); + _pool = new CommandBufferPool(queue); } return _pool; diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs index 075ac21fe..af1d42744 100644 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Metal/BufferHolder.cs @@ -8,7 +8,7 @@ using System.Threading; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class BufferHolder : IDisposable + class BufferHolder : IDisposable { private CacheByRange _cachedConvertedBuffers; diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs index bf7f00901..766c1dea2 100644 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -8,7 +8,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - public readonly struct ScopedTemporaryBuffer : IDisposable + readonly struct ScopedTemporaryBuffer : IDisposable { private readonly BufferManager _bufferManager; private readonly bool _isReserved; @@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - public class BufferManager : IDisposable + class BufferManager : IDisposable { private readonly IdList _buffers; diff --git a/src/Ryujinx.Graphics.Metal/CacheByRange.cs b/src/Ryujinx.Graphics.Metal/CacheByRange.cs index d507dcaeb..39255de39 100644 --- a/src/Ryujinx.Graphics.Metal/CacheByRange.cs +++ b/src/Ryujinx.Graphics.Metal/CacheByRange.cs @@ -1,11 +1,10 @@ -using SharpMetal.Metal; using System; using System.Collections.Generic; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - public interface ICacheKey : IDisposable + interface ICacheKey : IDisposable { bool KeyEqual(ICacheKey other); } @@ -116,7 +115,7 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - public readonly struct Dependency + readonly struct Dependency { private readonly BufferHolder _buffer; private readonly int _offset; diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs index aa659775a..925e4980b 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs @@ -7,14 +7,12 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class CommandBufferPool : IDisposable + class CommandBufferPool : IDisposable { public const int MaxCommandBuffers = 16; private readonly int _totalCommandBuffers; private readonly int _totalCommandBuffersMask; - - private readonly MTLDevice _device; private readonly MTLCommandQueue _queue; [SupportedOSPlatform("macos")] @@ -45,9 +43,8 @@ namespace Ryujinx.Graphics.Metal private int _queuedCount; private int _inUseCount; - public CommandBufferPool(MTLDevice device, MTLCommandQueue queue) + public CommandBufferPool(MTLCommandQueue queue) { - _device = device; _queue = queue; _totalCommandBuffers = MaxCommandBuffers; diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs b/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs index 48c4d3a1e..43cea6fe9 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs @@ -5,7 +5,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public readonly struct CommandBufferScoped : IDisposable + readonly struct CommandBufferScoped : IDisposable { private readonly CommandBufferPool _pool; public MTLCommandBuffer CommandBuffer { get; } diff --git a/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs b/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs index c35b580eb..a76f4c33c 100644 --- a/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs +++ b/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs @@ -7,7 +7,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class ComputePipelineCache : StateCache + class ComputePipelineCache : StateCache { private readonly MTLDevice _device; diff --git a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs index 1964d093b..be47653c0 100644 --- a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs +++ b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs @@ -4,7 +4,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public struct DepthStencilHash + struct DepthStencilHash { public struct StencilHash { @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - public class DepthStencilCache : StateCache + class DepthStencilCache : StateCache { private readonly MTLDevice _device; diff --git a/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs b/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs index cb7760c1e..a2d2247c4 100644 --- a/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs @@ -5,7 +5,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public readonly struct DisposableBuffer : IDisposable + readonly struct DisposableBuffer : IDisposable { public MTLBuffer Value { get; } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index f99f52aec..72422c70d 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -5,7 +5,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - public struct DirtyFlags + struct DirtyFlags { public bool RenderPipeline = false; public bool ComputePipeline = false; @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Metal } } - public record struct BufferRef + record struct BufferRef { public Auto Buffer; public int Index; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 9919db5f2..923f562bd 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -360,7 +360,7 @@ namespace Ryujinx.Graphics.Metal { _currentState.IndexType = MTLIndexType.UInt16; _currentState.IndexBufferOffset = (ulong)buffer.Offset; - _currentState.IndexBuffer = _bufferManager.GetBufferI8ToI16(_pipeline.CurrentCommandBuffer, buffer.Handle, buffer.Offset, buffer.Size); + _currentState.IndexBuffer = _bufferManager.GetBufferI8ToI16(_pipeline.Cbs, buffer.Handle, buffer.Offset, buffer.Size); } else { @@ -1036,12 +1036,12 @@ namespace Ryujinx.Graphics.Metal if (range.HasValue) { offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer, offset, range.Value.Size, range.Value.Write).Value; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; } else { - mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer).Value; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; } renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)index); @@ -1072,12 +1072,12 @@ namespace Ryujinx.Graphics.Metal if (range.HasValue) { offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer, offset, range.Value.Size, range.Value.Write).Value; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; } else { - mtlBuffer = autoBuffer.Get(_pipeline.CurrentCommandBuffer).Value; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; } computeCommandEncoder.SetBuffer(mtlBuffer, (ulong)offset, (ulong)index); diff --git a/src/Ryujinx.Graphics.Metal/FenceHolder.cs b/src/Ryujinx.Graphics.Metal/FenceHolder.cs index 3f11fd971..a8dd28c0d 100644 --- a/src/Ryujinx.Graphics.Metal/FenceHolder.cs +++ b/src/Ryujinx.Graphics.Metal/FenceHolder.cs @@ -6,7 +6,7 @@ using System.Threading; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class FenceHolder : IDisposable + class FenceHolder : IDisposable { private MTLCommandBuffer _fence; private int _referenceCount; diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 0673a42c2..dcb5a4f62 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -10,7 +10,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class HelperShader : IDisposable + class HelperShader : IDisposable { private const int ConvertElementsPerWorkgroup = 32 * 100; // Work group size of 32 times 100 elements. private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders"; @@ -268,7 +268,7 @@ namespace Ryujinx.Graphics.Metal // TODO: Flush - using var buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.CurrentCommandBuffer, ClearColorBufferSize); + using var buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearColorBufferSize); buffer.Holder.SetDataUnchecked(buffer.Offset, clearColor); _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 08d1ca540..c68da5a4a 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -21,7 +21,6 @@ namespace Ryujinx.Graphics.Metal public event EventHandler ScreenCaptured; public bool PreferThreading => true; - public IPipeline Pipeline => _pipeline; public IWindow Window => _window; @@ -54,9 +53,9 @@ namespace Ryujinx.Graphics.Metal layer.Device = _device; layer.FramebufferOnly = false; - CommandBufferPool = new CommandBufferPool(_device, _queue); + CommandBufferPool = new CommandBufferPool(_queue); _window = new Window(this, layer); - _pipeline = new Pipeline(_device, this, _queue); + _pipeline = new Pipeline(_device, this); BufferManager = new BufferManager(_device, this, _pipeline); _pipeline.InitEncoderStateManager(BufferManager); @@ -222,7 +221,7 @@ namespace Ryujinx.Graphics.Metal public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) { - BufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPassDelegate); + BufferManager.SetData(buffer, offset, data, _pipeline.Cbs, _pipeline.EndRenderPassDelegate); } public void UpdateCounters() diff --git a/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs index 481580b8e..cd5ad08ba 100644 --- a/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs +++ b/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Metal /// Holder for multiple host GPU fences. /// [SupportedOSPlatform("macos")] - public class MultiFenceHolder + class MultiFenceHolder { private const int BufferUsageTrackingGranularity = 4096; diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 7a83a02a7..3a913986b 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -18,40 +18,30 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - public class Pipeline : IPipeline, IDisposable + class Pipeline : IPipeline, IDisposable { private readonly MTLDevice _device; - private readonly MTLCommandQueue _commandQueue; private readonly MetalRenderer _renderer; - - private CommandBufferScoped Cbs; - private CommandBufferScoped? PreloadCbs; - public MTLCommandBuffer CommandBuffer; - - public readonly Action EndRenderPassDelegate; - - public CommandBufferScoped CurrentCommandBuffer => Cbs; - - private MTLCommandEncoder? _currentEncoder; - public MTLCommandEncoder? CurrentEncoder => _currentEncoder; - - private EncoderType _currentEncoderType = EncoderType.None; - public EncoderType CurrentEncoderType => _currentEncoderType; - private EncoderStateManager _encoderStateManager; - public Pipeline(MTLDevice device, MetalRenderer renderer, MTLCommandQueue commandQueue) + public readonly Action EndRenderPassDelegate; + public MTLCommandBuffer CommandBuffer; + + internal CommandBufferScoped Cbs { get; private set; } + internal MTLCommandEncoder? CurrentEncoder { get; private set; } + internal EncoderType CurrentEncoderType { get; private set; } = EncoderType.None; + + public Pipeline(MTLDevice device, MetalRenderer renderer) { _device = device; _renderer = renderer; - _commandQueue = commandQueue; EndRenderPassDelegate = EndCurrentPass; CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer; } - public void InitEncoderStateManager(BufferManager bufferManager) + internal void InitEncoderStateManager(BufferManager bufferManager) { _encoderStateManager = new EncoderStateManager(_device, bufferManager, this); } @@ -79,13 +69,13 @@ namespace Ryujinx.Graphics.Metal public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false) { MTLRenderCommandEncoder renderCommandEncoder; - if (_currentEncoder == null || _currentEncoderType != EncoderType.Render) + if (CurrentEncoder == null || CurrentEncoderType != EncoderType.Render) { renderCommandEncoder = BeginRenderPass(); } else { - renderCommandEncoder = new MTLRenderCommandEncoder(_currentEncoder.Value); + renderCommandEncoder = new MTLRenderCommandEncoder(CurrentEncoder.Value); } if (forDraw) @@ -98,11 +88,11 @@ namespace Ryujinx.Graphics.Metal public MTLBlitCommandEncoder GetOrCreateBlitEncoder() { - if (_currentEncoder != null) + if (CurrentEncoder != null) { - if (_currentEncoderType == EncoderType.Blit) + if (CurrentEncoderType == EncoderType.Blit) { - return new MTLBlitCommandEncoder(_currentEncoder.Value); + return new MTLBlitCommandEncoder(CurrentEncoder.Value); } } @@ -112,13 +102,13 @@ namespace Ryujinx.Graphics.Metal public MTLComputeCommandEncoder GetOrCreateComputeEncoder() { MTLComputeCommandEncoder computeCommandEncoder; - if (_currentEncoder == null || _currentEncoderType != EncoderType.Compute) + if (CurrentEncoder == null || CurrentEncoderType != EncoderType.Compute) { computeCommandEncoder = BeginComputePass(); } else { - computeCommandEncoder = new MTLComputeCommandEncoder(_currentEncoder.Value); + computeCommandEncoder = new MTLComputeCommandEncoder(CurrentEncoder.Value); } _encoderStateManager.RebindComputeState(computeCommandEncoder); @@ -128,62 +118,62 @@ namespace Ryujinx.Graphics.Metal public void EndCurrentPass() { - if (_currentEncoder != null) + if (CurrentEncoder != null) { - switch (_currentEncoderType) + switch (CurrentEncoderType) { case EncoderType.Blit: - new MTLBlitCommandEncoder(_currentEncoder.Value).EndEncoding(); - _currentEncoder = null; + new MTLBlitCommandEncoder(CurrentEncoder.Value).EndEncoding(); + CurrentEncoder = null; break; case EncoderType.Compute: - new MTLComputeCommandEncoder(_currentEncoder.Value).EndEncoding(); - _currentEncoder = null; + new MTLComputeCommandEncoder(CurrentEncoder.Value).EndEncoding(); + CurrentEncoder = null; break; case EncoderType.Render: - new MTLRenderCommandEncoder(_currentEncoder.Value).EndEncoding(); - _currentEncoder = null; + new MTLRenderCommandEncoder(CurrentEncoder.Value).EndEncoding(); + CurrentEncoder = null; break; default: throw new ArgumentOutOfRangeException(); } - _currentEncoderType = EncoderType.None; + CurrentEncoderType = EncoderType.None; } } - public MTLRenderCommandEncoder BeginRenderPass() + private MTLRenderCommandEncoder BeginRenderPass() { EndCurrentPass(); var renderCommandEncoder = _encoderStateManager.CreateRenderCommandEncoder(); - _currentEncoder = renderCommandEncoder; - _currentEncoderType = EncoderType.Render; + CurrentEncoder = renderCommandEncoder; + CurrentEncoderType = EncoderType.Render; return renderCommandEncoder; } - public MTLBlitCommandEncoder BeginBlitPass() + private MTLBlitCommandEncoder BeginBlitPass() { EndCurrentPass(); var descriptor = new MTLBlitPassDescriptor(); var blitCommandEncoder = Cbs.CommandBuffer.BlitCommandEncoder(descriptor); - _currentEncoder = blitCommandEncoder; - _currentEncoderType = EncoderType.Blit; + CurrentEncoder = blitCommandEncoder; + CurrentEncoderType = EncoderType.Blit; return blitCommandEncoder; } - public MTLComputeCommandEncoder BeginComputePass() + private MTLComputeCommandEncoder BeginComputePass() { EndCurrentPass(); var computeCommandEncoder = _encoderStateManager.CreateComputeCommandEncoder(); - _currentEncoder = computeCommandEncoder; - _currentEncoderType = EncoderType.Compute; + CurrentEncoder = computeCommandEncoder; + CurrentEncoderType = EncoderType.Compute; return computeCommandEncoder; } @@ -232,7 +222,7 @@ namespace Ryujinx.Graphics.Metal public void Barrier() { - switch (_currentEncoderType) + switch (CurrentEncoderType) { case EncoderType.Render: { @@ -546,7 +536,7 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager.UpdateStorageBuffers(buffers); } - public void SetStorageBuffers(int first, ReadOnlySpan> buffers) + internal void SetStorageBuffers(int first, ReadOnlySpan> buffers) { _encoderStateManager.UpdateStorageBuffers(first, buffers); } diff --git a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs index 6fb171816..b8e6005c4 100644 --- a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs +++ b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs @@ -7,7 +7,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public struct RenderPipelineHash + struct RenderPipelineHash { public MTLFunction VertexFunction; public MTLFunction FragmentFunction; @@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - public class RenderPipelineCache : StateCache + class RenderPipelineCache : StateCache { private readonly MTLDevice _device; diff --git a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs index 81adcd116..07450f6b0 100644 --- a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs @@ -8,7 +8,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - public readonly struct StagingBufferReserved + readonly struct StagingBufferReserved { public readonly BufferHolder Buffer; public readonly int Offset; @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - public class StagingBuffer : IDisposable + class StagingBuffer : IDisposable { private const int BufferSize = 32 * 1024 * 1024; diff --git a/src/Ryujinx.Graphics.Metal/StateCache.cs b/src/Ryujinx.Graphics.Metal/StateCache.cs index f333814e6..9b8391ffc 100644 --- a/src/Ryujinx.Graphics.Metal/StateCache.cs +++ b/src/Ryujinx.Graphics.Metal/StateCache.cs @@ -5,7 +5,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public abstract class StateCache : IDisposable where T : IDisposable + abstract class StateCache : IDisposable where T : IDisposable { private readonly Dictionary _cache = new(); diff --git a/src/Ryujinx.Graphics.Metal/StringHelper.cs b/src/Ryujinx.Graphics.Metal/StringHelper.cs index 21cd474dc..46e8ad2e9 100644 --- a/src/Ryujinx.Graphics.Metal/StringHelper.cs +++ b/src/Ryujinx.Graphics.Metal/StringHelper.cs @@ -5,7 +5,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class StringHelper + class StringHelper { public static NSString NSString(string source) { diff --git a/src/Ryujinx.Graphics.Metal/SyncManager.cs b/src/Ryujinx.Graphics.Metal/SyncManager.cs index 528531575..0ec508365 100644 --- a/src/Ryujinx.Graphics.Metal/SyncManager.cs +++ b/src/Ryujinx.Graphics.Metal/SyncManager.cs @@ -7,7 +7,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class SyncManager + class SyncManager { private class SyncHandle { diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index da52e61e7..bb5a6bb5f 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -8,7 +8,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public class Texture : TextureBase, ITexture + class Texture : TextureBase, ITexture { public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) { @@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(BufferRange range, int layer, int level, int stride) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); - var cbs = _pipeline.CurrentCommandBuffer; + var cbs = _pipeline.Cbs; int outSize = Info.GetMipSize(level); diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index 4b71ec9f9..51f5ec8d2 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -6,7 +6,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - public abstract class TextureBase : IDisposable + abstract class TextureBase : IDisposable { private bool _disposed; -- 2.47.1 From d0e4adac367f96bc5cd021cd3340111efb1f125d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 00:54:04 +0100 Subject: [PATCH 241/368] PreloadCbs + FlushCommandsIfWeightExceeding --- src/Ryujinx.Graphics.Metal/BufferHolder.cs | 14 ++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 39 ++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs index af1d42744..f07143a43 100644 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Metal/BufferHolder.cs @@ -190,6 +190,18 @@ namespace Ryujinx.Graphics.Metal } } + if (cbs != null && + _pipeline.RenderPassActive && + !(_buffer.HasCommandBufferDependency(cbs.Value) && + _waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize))) + { + // If the buffer hasn't been used on the command buffer yet, try to preload the data. + // This avoids ending and beginning render passes on each buffer data upload. + + cbs = _pipeline.PreloadCbs; + endRenderPass = null; + } + if (allowCbsWait) { _renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, endRenderPass, this, offset, data); @@ -331,6 +343,8 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { + _pipeline.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); + _buffer.Dispose(); _cachedConvertedBuffers.Dispose(); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 3a913986b..67c3da891 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -20,16 +20,21 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Pipeline : IPipeline, IDisposable { + private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB + private readonly MTLDevice _device; private readonly MetalRenderer _renderer; private EncoderStateManager _encoderStateManager; + private ulong _byteWeight; public readonly Action EndRenderPassDelegate; public MTLCommandBuffer CommandBuffer; + internal CommandBufferScoped? PreloadCbs { get; private set; } internal CommandBufferScoped Cbs { get; private set; } internal MTLCommandEncoder? CurrentEncoder { get; private set; } internal EncoderType CurrentEncoderType { get; private set; } = EncoderType.None; + internal bool RenderPassActive { get; private set; } public Pipeline(MTLDevice device, MetalRenderer renderer) { @@ -133,6 +138,7 @@ namespace Ryujinx.Graphics.Metal case EncoderType.Render: new MTLRenderCommandEncoder(CurrentEncoder.Value).EndEncoding(); CurrentEncoder = null; + RenderPassActive = false; break; default: throw new ArgumentOutOfRangeException(); @@ -150,6 +156,7 @@ namespace Ryujinx.Graphics.Metal CurrentEncoder = renderCommandEncoder; CurrentEncoderType = EncoderType.Render; + RenderPassActive = true; return renderCommandEncoder; } @@ -198,12 +205,44 @@ namespace Ryujinx.Graphics.Metal dst.Dispose(); } + public void FlushCommandsIfWeightExceeding(IAuto disposedResource, ulong byteWeight) + { + bool usedByCurrentCb = disposedResource.HasCommandBufferDependency(Cbs); + + if (PreloadCbs != null && !usedByCurrentCb) + { + usedByCurrentCb = disposedResource.HasCommandBufferDependency(PreloadCbs.Value); + } + + if (usedByCurrentCb) + { + // Since we can only free memory after the command buffer that uses a given resource was executed, + // keeping the command buffer might cause a high amount of memory to be in use. + // To prevent that, we force submit command buffers if the memory usage by resources + // in use by the current command buffer is above a given limit, and those resources were disposed. + _byteWeight += byteWeight; + + if (_byteWeight >= MinByteWeightForFlush) + { + FlushCommandsImpl(); + } + } + } + public void FlushCommandsImpl() { SaveState(); EndCurrentPass(); + _byteWeight = 0; + + if (PreloadCbs != null) + { + PreloadCbs.Value.Dispose(); + PreloadCbs = null; + } + CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; _renderer.RegisterFlush(); -- 2.47.1 From becf828d0a9c3239cf9f2491408c5e8d320b330c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 10:31:21 +0100 Subject: [PATCH 242/368] Instruction.Barrier Whoops Fix inline functions in compute stage Fix regression Declare SharedMemories + Only Declare Memories on Main Func Lowecase struct Avoid magic strings Make function signatures readable Change how unsized arrays are indexed Use string builder Fix shuffle instructions Cleanup NumberFormater Bunch of Subgroup I/O Vars Will probably need further refinement Fix point_coord type Fix support buffer declaration Fix point_coord --- .../CodeGen/Msl/CodeGenContext.cs | 2 +- .../CodeGen/Msl/Declarations.cs | 41 +++++++++++----- .../CodeGen/Msl/DefaultNames.cs | 2 + .../CodeGen/Msl/Instructions/InstGen.cs | 25 ++++++---- .../CodeGen/Msl/Instructions/InstGenCall.cs | 17 +++++-- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 8 ++-- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 10 +--- .../CodeGen/Msl/Instructions/IoMap.cs | 10 +++- .../CodeGen/Msl/MslGenerator.cs | 24 +++++++--- .../CodeGen/Msl/NumberFormatter.cs | 48 ++++++++----------- 10 files changed, 110 insertions(+), 77 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index 79c13964c..0ae6313eb 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string Tab = " "; // The number of additional arguments that every function (except for the main one) must have (for instance support_buffer) - public const int AdditionalArgCount = 2; + public const int AdditionalArgCount = 1; public StructuredFunction CurrentFunction { get; set; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 8d4a9c877..60729ac60 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -64,9 +64,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return ioDefinition.StorageKind == storageKind && ioDefinition.IoVariable == IoVariable.UserDefined; } - public static void DeclareLocals(CodeGenContext context, StructuredFunction function, ShaderStage stage) + public static void DeclareLocals(CodeGenContext context, StructuredFunction function, ShaderStage stage, bool isMainFunc = false) { - DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); + if (isMainFunc) + { + DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); + DeclareMemories(context, context.Properties.SharedMemories.Values, isShared: true); + } + switch (stage) { case ShaderStage.Vertex: @@ -112,6 +117,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl private static void DeclareMemories(CodeGenContext context, IEnumerable memories, bool isShared) { + string prefix = isShared ? "threadgroup " : string.Empty; + foreach (var memory in memories) { string arraySize = ""; @@ -120,7 +127,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl arraySize = $"[{memory.ArrayLength}]"; } var typeName = GetVarTypeName(context, memory.Type & ~AggregateType.Array); - context.AppendLine($"{typeName} {memory.Name}{arraySize};"); + context.AppendLine($"{prefix}{typeName} {memory.Name}{arraySize};"); } } @@ -128,23 +135,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { foreach (BufferDefinition buffer in buffers) { - context.AppendLine($"struct Struct_{buffer.Name}"); + context.AppendLine($"struct {DefaultNames.StructPrefix}_{buffer.Name}"); context.EnterScope(); foreach (StructureField field in buffer.Type.Fields) { - if (field.Type.HasFlag(AggregateType.Array) && field.ArrayLength > 0) - { - string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); + string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); + string arraySuffix = ""; - context.AppendLine($"{typeName} {field.Name}[{field.ArrayLength}];"); - } - else + if (field.Type.HasFlag(AggregateType.Array)) { - string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); - - context.AppendLine($"{typeName} {field.Name};"); + if (field.ArrayLength > 0) + { + arraySuffix = $"[{field.ArrayLength}]"; + } + else + { + // Probably UB, but this is the approach that MVK takes + arraySuffix = "[1]"; + } } + + context.AppendLine($"{typeName} {field.Name}{arraySuffix};"); } context.LeaveScope(";"); @@ -191,6 +203,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.GlobalId => "uint3", IoVariable.VertexId => "uint", IoVariable.VertexIndex => "uint", + IoVariable.PointCoord => "float2", _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) }; string name = ioDefinition.IoVariable switch @@ -199,6 +212,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.GlobalId => "global_id", IoVariable.VertexId => "vertex_id", IoVariable.VertexIndex => "vertex_index", + IoVariable.PointCoord => "point_coord", _ => $"{DefaultNames.IAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch @@ -208,6 +222,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.VertexId => "[[vertex_id]]", // TODO: Avoid potential redeclaration IoVariable.VertexIndex => "[[vertex_id]]", + IoVariable.PointCoord => "[[point_coord]]", IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]", _ => "" }; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs index 8a468395e..0b946c3aa 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string IAttributePrefix = "inAttr"; public const string OAttributePrefix = "outAttr"; + public const string StructPrefix = "struct"; + public const string ArgumentNamePrefix = "a"; public const string UndefinedName = "0"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 8c101ad75..696564992 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -2,7 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; - +using System.Text; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenCall; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenMemory; @@ -39,11 +39,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions int arity = (int)(info.Type & InstType.ArityMask); - string args = string.Empty; + StringBuilder builder = new(); - if (atomic) + if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory)) { - // Hell + builder.Append(GenerateLoadOrStore(context, operation, isStore: false)); + + AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32 + ? AggregateType.S32 + : AggregateType.U32; + + for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++) + { + builder.Append($", {GetSourceExpr(context, operation.GetSource(argIndex), dstType)}"); + } } else { @@ -51,16 +60,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { if (argIndex != 0) { - args += ", "; + builder.Append(", "); } AggregateType dstType = GetSrcVarType(inst, argIndex); - args += GetSourceExpr(context, operation.GetSource(argIndex), dstType); + builder.Append(GetSourceExpr(context, operation.GetSource(argIndex), dstType)); } } - return info.OpName + '(' + args + ')'; + return $"{info.OpName}({builder})"; } else if ((info.Type & InstType.Op) != 0) { @@ -110,7 +119,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions switch (inst & Instruction.Mask) { case Instruction.Barrier: - return "|| BARRIER ||"; + return "threadgroup_barrier(mem_flags::mem_threadgroup)"; case Instruction.Call: return Call(context, operation); case Instruction.FSIBegin: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs index 5df3aa282..c063ff458 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs @@ -13,13 +13,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions var functon = context.GetFunction(funcId.Value); int argCount = operation.SourcesCount - 1; - string[] args = new string[argCount + CodeGenContext.AdditionalArgCount]; + int additionalArgCount = CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); + + string[] args = new string[argCount + additionalArgCount]; // Additional arguments - args[0] = "in"; - args[1] = "support_buffer"; + if (context.Definitions.Stage != ShaderStage.Compute) + { + args[0] = "in"; + args[1] = "support_buffer"; + } + else + { + args[0] = "support_buffer"; + } - int argIndex = CodeGenContext.AdditionalArgCount; + int argIndex = additionalArgCount; for (int i = 0; i < argCount; i++) { args[argIndex++] = GetSourceExpr(context, operation.GetSource(i + 1), functon.GetArgumentType(i)); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 406fda11a..014d070ef 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -109,10 +109,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3); Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3); Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3); - Add(Instruction.Shuffle, InstType.CallQuaternary, "simd_shuffle"); - Add(Instruction.ShuffleDown, InstType.CallQuaternary, "simd_shuffle_down"); - Add(Instruction.ShuffleUp, InstType.CallQuaternary, "simd_shuffle_up"); - Add(Instruction.ShuffleXor, InstType.CallQuaternary, "simd_shuffle_xor"); + Add(Instruction.Shuffle, InstType.CallBinary, "simd_shuffle"); + Add(Instruction.ShuffleDown, InstType.CallBinary, "simd_shuffle_down"); + Add(Instruction.ShuffleUp, InstType.CallBinary, "simd_shuffle_up"); + Add(Instruction.ShuffleXor, InstType.CallBinary, "simd_shuffle_xor"); Add(Instruction.Sine, InstType.CallUnary, "sin"); Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); Add(Instruction.Store, InstType.Special); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 135cd80e0..bb1a69939 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -47,15 +47,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions StructureField field = buffer.Type.Fields[fieldIndex.Value]; varName = buffer.Name; - if ((field.Type & AggregateType.Array) != 0 && field.ArrayLength == 0) - { - // Unsized array, the buffer is indexed instead of the field - fieldName = "." + field.Name; - } - else - { - varName += "->" + field.Name; - } + varName += "->" + field.Name; varType = field.Type; break; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 2e93310aa..1561271d0 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -27,13 +27,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.GlobalId => ("thread_position_in_grid", AggregateType.Vector3 | AggregateType.U32), IoVariable.InstanceId => ("instance_id", AggregateType.S32), IoVariable.InvocationId => ("INVOCATION_ID", AggregateType.S32), - IoVariable.PointCoord => ("point_coord", AggregateType.Vector2), + IoVariable.PointCoord => ("point_coord", AggregateType.Vector2 | AggregateType.FP32), IoVariable.PointSize => ("out.point_size", AggregateType.FP32), IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), + IoVariable.SubgroupEqMask => ("thread_index_in_simdgroup >= 32 ? uint4(0, (1 << (thread_index_in_simdgroup - 32)), uint2(0)) : uint4(1 << thread_index_in_simdgroup, uint3(0))", AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupGeMask => ("uint4(insert_bits(0u, 0xFFFFFFFF, thread_index_in_simdgroup, 32 - thread_index_in_simdgroup), uint3(0)) & (uint4((uint)((simd_vote::vote_t)simd_ballot(true) & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)simd_ballot(true) >> 32) & 0xFFFFFFFF), 0, 0))", AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupGtMask => ("uint4(insert_bits(0u, 0xFFFFFFFF, thread_index_in_simdgroup + 1, 32 - thread_index_in_simdgroup - 1), uint3(0)) & (uint4((uint)((simd_vote::vote_t)simd_ballot(true) & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)simd_ballot(true) >> 32) & 0xFFFFFFFF), 0, 0))", AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupLaneId => ("thread_index_in_simdgroup", AggregateType.U32), + IoVariable.SubgroupLeMask => ("uint4(extract_bits(0xFFFFFFFF, 0, min(thread_index_in_simdgroup + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)thread_index_in_simdgroup + 1 - 32, 0)), uint2(0))", AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupLtMask => ("uint4(extract_bits(0xFFFFFFFF, 0, min(thread_index_in_simdgroup, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)thread_index_in_simdgroup - 32, 0)), uint2(0))", AggregateType.Vector4 | AggregateType.U32), + IoVariable.ThreadKill => ("simd_is_helper_thread()", AggregateType.Bool), IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), IoVariable.ThreadId => ("thread_position_in_threadgroup", AggregateType.Vector3 | AggregateType.U32), - IoVariable.SubgroupLaneId => ("thread_index_in_simdgroup", AggregateType.U32), IoVariable.VertexId => ("vertex_id", AggregateType.S32), // gl_VertexIndex does not have a direct equivalent in MSL IoVariable.VertexIndex => ("vertex_id", AggregateType.U32), diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 87512a961..bb5ea5f6f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(GetFunctionSignature(context, function, stage, isMainFunc)); context.EnterScope(); - Declarations.DeclareLocals(context, function, stage); + Declarations.DeclareLocals(context, function, stage, isMainFunc); PrintBlock(context, function.MainBlock, isMainFunc); @@ -63,15 +63,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl ShaderStage stage, bool isMainFunc = false) { - int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount; + int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); string[] args = new string[additionalArgCount + function.InArguments.Length + function.OutArguments.Length]; // All non-main functions need to be able to access the support_buffer as well if (!isMainFunc) { - args[0] = "FragmentIn in"; - args[1] = "constant Struct_support_buffer* support_buffer"; + if (stage != ShaderStage.Compute) + { + args[0] = stage == ShaderStage.Vertex ? "VertexIn in" : "FragmentIn in"; + args[1] = $"constant {DefaultNames.StructPrefix}_support_buffer* support_buffer"; + } + else + { + args[0] = $"constant {DefaultNames.StructPrefix}_support_buffer* support_buffer"; + } } int argIndex = additionalArgCount; @@ -141,13 +148,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (var constantBuffer in context.Properties.ConstantBuffers.Values) { - args = args.Append($"constant Struct_{constantBuffer.Name}* {constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); + args = args.Append($"constant {DefaultNames.StructPrefix}_{constantBuffer.Name}* {constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); } foreach (var storageBuffers in context.Properties.StorageBuffers.Values) { // Offset the binding by 15 to avoid clashing with the constant buffers - args = args.Append($"device Struct_{storageBuffers.Name}* {storageBuffers.Name} [[buffer({storageBuffers.Binding + 15})]]").ToArray(); + args = args.Append($"device {DefaultNames.StructPrefix}_{storageBuffers.Name}* {storageBuffers.Name} [[buffer({storageBuffers.Binding + 15})]]").ToArray(); } foreach (var texture in context.Properties.Textures.Values) @@ -162,7 +169,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } - return $"{funcKeyword} {returnType} {funcName ?? function.Name}({string.Join(", ", args)})"; + var funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}("; + var indent = new string(' ', funcPrefix.Length); + + return $"{funcPrefix}{string.Join($", \n{indent}", args)})"; } private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs index f086e7436..63ecbc0aa 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs @@ -10,25 +10,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public static bool TryFormat(int value, AggregateType dstType, out string formatted) { - if (dstType == AggregateType.FP32) + switch (dstType) { - return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted); - } - else if (dstType == AggregateType.S32) - { - formatted = FormatInt(value); - } - else if (dstType == AggregateType.U32) - { - formatted = FormatUint((uint)value); - } - else if (dstType == AggregateType.Bool) - { - formatted = value != 0 ? "true" : "false"; - } - else - { - throw new ArgumentException($"Invalid variable type \"{dstType}\"."); + case AggregateType.FP32: + return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted); + case AggregateType.S32: + formatted = FormatInt(value); + break; + case AggregateType.U32: + formatted = FormatUint((uint)value); + break; + case AggregateType.Bool: + formatted = value != 0 ? "true" : "false"; + break; + default: + throw new ArgumentException($"Invalid variable type \"{dstType}\"."); } return true; @@ -65,18 +61,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public static string FormatInt(int value, AggregateType dstType) { - if (dstType == AggregateType.S32) + return dstType switch { - return FormatInt(value); - } - else if (dstType == AggregateType.U32) - { - return FormatUint((uint)value); - } - else - { - throw new ArgumentException($"Invalid variable type \"{dstType}\"."); - } + AggregateType.S32 => FormatInt(value), + AggregateType.U32 => FormatUint((uint)value), + _ => throw new ArgumentException($"Invalid variable type \"{dstType}\".") + }; } public static string FormatInt(int value) -- 2.47.1 From 0ebc8bd1b8a06469428053ad96ccd558077c0780 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 16:21:58 +0100 Subject: [PATCH 243/368] Fix Animal Crossing Crash --- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index bb5a6bb5f..f5b137a99 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Metal levels.length = (ulong)Info.Levels; NSRange slices; slices.location = (ulong)firstLayer; - slices.length = (ulong)Info.Depth; + slices.length = (ulong)info.GetDepthOrLayers(); var swizzle = GetSwizzle(info, pixelFormat); -- 2.47.1 From 58eefa8bdf57132107acc2a4f272e0f7c0cc35cf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 16:32:31 +0100 Subject: [PATCH 244/368] Big GetData() Co-authored-by: riperiperi --- src/Ryujinx.Graphics.Metal/Texture.cs | 58 ++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index f5b137a99..656e67811 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -195,7 +195,63 @@ namespace Ryujinx.Graphics.Metal public PinnedSpan GetData() { - throw new NotImplementedException(); + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + + ulong length = 0; + + for (int level = 0; level < Info.Levels; level++) + { + length += (ulong)Info.GetMipSize(level); + } + + unsafe + { + var mtlBuffer = _device.NewBuffer(length, MTLResourceOptions.ResourceStorageModeShared); + + int width = Info.Width; + int height = Info.Height; + int depth = Info.Depth; + int levels = Info.GetLevelsClamped(); + int layers = Info.GetLayers(); + bool is3D = Info.Target == Target.Texture3D; + + int offset = 0; + + for (int level = 0; level < levels; level++) + { + int mipSize = Info.GetMipSize2D(level); + int endOffset = offset + mipSize; + + for (int layer = 0; layer < layers; layer++) + { + blitCommandEncoder.CopyFromTexture( + _mtlTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin(), + new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, + mtlBuffer, + (ulong)offset, + (ulong)Info.GetMipStride(level), + (ulong)mipSize + ); + + offset += mipSize; + } + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (is3D) + { + depth = Math.Max(1, depth >> 1); + } + } + + // TODO: wait + + return new PinnedSpan(mtlBuffer.Contents.ToPointer(), (int)length, () => mtlBuffer.Dispose()); + } } public PinnedSpan GetData(int layer, int level) -- 2.47.1 From 9b138a413cf471e9d58584d379d3da59e1a9d90a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 16:39:27 +0100 Subject: [PATCH 245/368] Actually clear the right render target --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 2 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 923f562bd..52d8df57c 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Metal public readonly MTLIndexType IndexType => _currentState.IndexType; public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; public readonly PrimitiveTopology Topology => _currentState.Topology; - public readonly Texture RenderTarget => _currentState.RenderTargets[0]; + public readonly Texture[] RenderTargets => _currentState.RenderTargets; public readonly Texture DepthStencil => _currentState.DepthStencil; // RGBA32F is the biggest format diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 67c3da891..dd4a15c6d 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -307,7 +307,7 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; - var dst = _encoderStateManager.RenderTarget; + var dst = _encoderStateManager.RenderTargets[index]; // TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format if (dst == null) -- 2.47.1 From fac2cbbbbf3a5f7ad20a54fce3825c5b5bbfb8d5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 16:58:58 +0100 Subject: [PATCH 246/368] =?UTF-8?q?Fix=20vertex=20=E2=80=9Cbuilt-ins?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only declare main func out in main Fix simd_ballot Fix thread_index_in_simdgroup outside of compute Fix atomic operations instance_index --- .../CodeGen/Msl/Declarations.cs | 37 +++++++++++++------ .../CodeGen/Msl/Instructions/InstGen.cs | 10 +++-- .../CodeGen/Msl/Instructions/InstGenBallot.cs | 21 +++++++++++ .../CodeGen/Msl/Instructions/InstGenHelper.cs | 14 +++---- .../CodeGen/Msl/Instructions/IoMap.cs | 7 ++-- .../CodeGen/Msl/MslGenerator.cs | 2 + 6 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 60729ac60..0b6aadd03 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -70,16 +70,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); DeclareMemories(context, context.Properties.SharedMemories.Values, isShared: true); - } - switch (stage) - { - case ShaderStage.Vertex: - context.AppendLine("VertexOut out;"); - break; - case ShaderStage.Fragment: - context.AppendLine("FragmentOut out;"); - break; + switch (stage) + { + case ShaderStage.Vertex: + context.AppendLine("VertexOut out;"); + // TODO: Only add if necessary + context.AppendLine("uint instance_index = instance_id + base_instance;"); + break; + case ShaderStage.Fragment: + context.AppendLine("FragmentOut out;"); + break; + } + + // TODO: Only add if necessary + if (stage != ShaderStage.Compute) + { + // MSL does not give us access to [[thread_index_in_simdgroup]] + // outside compute. But we may still need to provide this value in frag/vert. + context.AppendLine("uint thread_index_in_simdgroup = simd_prefix_exclusive_sum(1);"); + } } foreach (AstOperand decl in function.Locals) @@ -90,15 +100,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } - public static string GetVarTypeName(CodeGenContext context, AggregateType type) + public static string GetVarTypeName(CodeGenContext context, AggregateType type, bool atomic = false) { + var s32 = atomic ? "atomic_int" : "int"; + var u32 = atomic ? "atomic_uint" : "uint"; + return type switch { AggregateType.Void => "void", AggregateType.Bool => "bool", AggregateType.FP32 => "float", - AggregateType.S32 => "int", - AggregateType.U32 => "uint", + AggregateType.S32 => s32, + AggregateType.U32 => u32, AggregateType.Vector2 | AggregateType.Bool => "bool2", AggregateType.Vector2 | AggregateType.FP32 => "float2", AggregateType.Vector2 | AggregateType.S32 => "int2", diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 696564992..0bea4d1aa 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; using System.Text; +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenBallot; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenCall; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenMemory; @@ -43,15 +44,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory)) { - builder.Append(GenerateLoadOrStore(context, operation, isStore: false)); - AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32 ? AggregateType.S32 : AggregateType.U32; + builder.Append($"(device {Declarations.GetVarTypeName(context, dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); + + for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++) { - builder.Append($", {GetSourceExpr(context, operation.GetSource(argIndex), dstType)}"); + builder.Append($", {GetSourceExpr(context, operation.GetSource(argIndex), dstType)}, memory_order_relaxed"); } } else @@ -118,6 +120,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { switch (inst & Instruction.Mask) { + case Instruction.Ballot: + return Ballot(context, operation); case Instruction.Barrier: return "threadgroup_barrier(mem_flags::mem_threadgroup)"; case Instruction.Call: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs new file mode 100644 index 000000000..1f53c74ed --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + static class InstGenBallot + { + public static string Ballot(CodeGenContext context, AstOperation operation) + { + AggregateType dstType = GetSrcVarType(operation.Inst, 0); + + string arg = GetSourceExpr(context, operation.GetSource(0), dstType); + char component = "xyzw"[operation.Index]; + + return $"uint4(as_type((simd_vote::vote_t)simd_ballot({arg})), 0, 0).{component}"; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 014d070ef..d230e2ed4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -15,17 +15,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions _infoTable = new InstInfo[(int)Instruction.Count]; #pragma warning disable IDE0055 // Disable formatting - Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomic_add_explicit"); - Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomic_and_explicit"); + Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomic_fetch_add_explicit"); + Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomic_fetch_and_explicit"); Add(Instruction.AtomicCompareAndSwap, InstType.AtomicBinary, "atomic_compare_exchange_weak_explicit"); - Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "atomic_max_explicit"); - Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "atomic_min_explicit"); - Add(Instruction.AtomicOr, InstType.AtomicBinary, "atomic_or_explicit"); + Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "atomic_fetch_max_explicit"); + Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "atomic_fetch_min_explicit"); + Add(Instruction.AtomicOr, InstType.AtomicBinary, "atomic_fetch_or_explicit"); Add(Instruction.AtomicSwap, InstType.AtomicBinary, "atomic_exchange_explicit"); - Add(Instruction.AtomicXor, InstType.AtomicBinary, "atomic_xor_explicit"); + Add(Instruction.AtomicXor, InstType.AtomicBinary, "atomic_fetch_xor_explicit"); Add(Instruction.Absolute, InstType.CallUnary, "abs"); Add(Instruction.Add, InstType.OpBinaryCom, "+", 2); - Add(Instruction.Ballot, InstType.CallUnary, "simd_ballot"); + Add(Instruction.Ballot, InstType.Special); Add(Instruction.Barrier, InstType.Special); Add(Instruction.BitCount, InstType.CallUnary, "popcount"); Add(Instruction.BitfieldExtractS32, InstType.CallTernary, "extract_bits"); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 1561271d0..f9d0a96d9 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -17,15 +17,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { var returnValue = ioVariable switch { - IoVariable.BaseInstance => ("base_instance", AggregateType.S32), - IoVariable.BaseVertex => ("base_vertex", AggregateType.S32), + IoVariable.BaseInstance => ("base_instance", AggregateType.U32), + IoVariable.BaseVertex => ("base_vertex", AggregateType.U32), IoVariable.CtaId => ("threadgroup_position_in_grid", AggregateType.Vector3 | AggregateType.U32), IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), IoVariable.FragmentOutputColor => ($"out.color{location}", AggregateType.Vector4 | AggregateType.FP32), IoVariable.FragmentOutputDepth => ("out.depth", AggregateType.FP32), IoVariable.FrontFacing => ("in.front_facing", AggregateType.Bool), IoVariable.GlobalId => ("thread_position_in_grid", AggregateType.Vector3 | AggregateType.U32), - IoVariable.InstanceId => ("instance_id", AggregateType.S32), + IoVariable.InstanceId => ("instance_id", AggregateType.U32), + IoVariable.InstanceIndex => ("instance_index", AggregateType.U32), IoVariable.InvocationId => ("INVOCATION_ID", AggregateType.S32), IoVariable.PointCoord => ("point_coord", AggregateType.Vector2 | AggregateType.FP32), IoVariable.PointSize => ("out.point_size", AggregateType.FP32), diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index bb5ea5f6f..a3e09d3cb 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -137,6 +137,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { args = args.Append("uint vertex_id [[vertex_id]]").ToArray(); args = args.Append("uint instance_id [[instance_id]]").ToArray(); + args = args.Append("uint base_instance [[base_instance]]").ToArray(); + args = args.Append("uint base_vertex [[base_vertex]]").ToArray(); } else if (stage == ShaderStage.Compute) { -- 2.47.1 From a1ab7fe6a2a6ddb9f0cc9db9aa3618a472cd68c2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 22 Jun 2024 14:38:09 +0100 Subject: [PATCH 247/368] VoteAllEqual, FindLSB/MSB --- .../CodeGen/Msl/Declarations.cs | 26 +++++++++++++++++++ .../CodeGen/Msl/HelperFunctions/FindLSB.metal | 5 ++++ .../Msl/HelperFunctions/FindMSBS32.metal | 5 ++++ .../Msl/HelperFunctions/FindMSBU32.metal | 6 +++++ .../HelperFunctions/HelperFunctionNames.cs | 4 ++- .../Msl/HelperFunctions/VoteAllEqual.metal | 4 --- .../CodeGen/Msl/Instructions/InstGen.cs | 13 ++++------ .../CodeGen/Msl/Instructions/InstGenBallot.cs | 9 +++++++ .../Msl/Instructions/InstGenBarrier.cs | 16 ++++++++++++ .../CodeGen/Msl/Instructions/InstGenHelper.cs | 9 +++---- .../Ryujinx.Graphics.Shader.csproj | 8 +++--- .../StructuredIr/HelperFunctionsMask.cs | 5 ++++ .../StructuredIr/StructuredProgram.cs | 14 ++++++++-- 13 files changed, 101 insertions(+), 23 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/VoteAllEqual.metal create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 0b6aadd03..fc199da2c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; @@ -57,6 +58,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(); DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values); DeclareBufferStructures(context, context.Properties.StorageBuffers.Values); + + if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) + { + AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal"); + } + + if ((info.HelperFunctionsMask & HelperFunctionsMask.FindMSBS32) != 0) + { + AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal"); + } + + if ((info.HelperFunctionsMask & HelperFunctionsMask.FindMSBU32) != 0) + { + AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal"); + } } static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) @@ -310,5 +326,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } } + + private static void AppendHelperFunction(CodeGenContext context, string filename) + { + string code = EmbeddedResources.ReadAllText(filename); + + code = code.Replace("\t", CodeGenContext.Tab); + + context.AppendLine(code); + context.AppendLine(); + } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal new file mode 100644 index 000000000..ad786adb3 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal @@ -0,0 +1,5 @@ +template +inline T findLSB(T x) +{ + return select(ctz(x), T(-1), x == T(0)); +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal new file mode 100644 index 000000000..af4eb6cbd --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal @@ -0,0 +1,5 @@ +template +inline T findMSBS32(T x) +{ + return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0)); +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal new file mode 100644 index 000000000..6d97c41a9 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal @@ -0,0 +1,6 @@ +template +inline T findMSBU32(T x) +{ + T v = select(x, T(-1) - x, x < T(0)); + return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs index 1e10f0721..a48da4990 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs @@ -2,6 +2,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { static class HelperFunctionNames { - public static string SwizzleAdd = "helperSwizzleAdd"; + public static string FindLSB = "findLSB"; + public static string FindMSBS32 = "findMSBS32"; + public static string FindMSBU32 = "findMSBU32"; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/VoteAllEqual.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/VoteAllEqual.metal deleted file mode 100644 index efbcee24d..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/VoteAllEqual.metal +++ /dev/null @@ -1,4 +0,0 @@ -inline bool voteAllEqual(bool value) -{ - return simd_all(value) || !simd_any(value); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 0bea4d1aa..6c983445b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Text; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenBallot; +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenBarrier; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenCall; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenMemory; @@ -123,19 +124,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.Ballot: return Ballot(context, operation); case Instruction.Barrier: - return "threadgroup_barrier(mem_flags::mem_threadgroup)"; + return Barrier(context, operation); case Instruction.Call: return Call(context, operation); case Instruction.FSIBegin: return "|| FSI BEGIN ||"; case Instruction.FSIEnd: return "|| FSI END ||"; - case Instruction.FindLSB: - return "|| FIND LSB ||"; - case Instruction.FindMSBS32: - return "|| FIND MSB S32 ||"; - case Instruction.FindMSBU32: - return "|| FIND MSB U32 ||"; case Instruction.GroupMemoryBarrier: return "|| FIND GROUP MEMORY BARRIER ||"; case Instruction.ImageLoad: @@ -152,6 +147,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return "|| MEMORY BARRIER ||"; case Instruction.Store: return Store(context, operation); + case Instruction.SwizzleAdd: + return "|| SWIZZLE ADD ||"; case Instruction.TextureSample: return TextureSample(context, operation); case Instruction.TextureQuerySamples: @@ -165,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.VectorExtract: return VectorExtract(context, operation); case Instruction.VoteAllEqual: - return "|| VOTE ALL EQUAL ||"; + return VoteAllEqual(context, operation); } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs index 1f53c74ed..19a065d77 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs @@ -17,5 +17,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return $"uint4(as_type((simd_vote::vote_t)simd_ballot({arg})), 0, 0).{component}"; } + + public static string VoteAllEqual(CodeGenContext context, AstOperation operation) + { + AggregateType dstType = GetSrcVarType(operation.Inst, 0); + + string arg = GetSourceExpr(context, operation.GetSource(0), dstType); + + return $"simd_all({arg}) || !simd_any({arg})"; + } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs new file mode 100644 index 000000000..7d681de26 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions +{ + static class InstGenBarrier + { + public static string Barrier(CodeGenContext context, AstOperation operation) + { + return "threadgroup_barrier(mem_flags::mem_threadgroup)"; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index d230e2ed4..68ec872af 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -71,10 +71,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.ExponentB2, InstType.CallUnary, "exp2"); Add(Instruction.FSIBegin, InstType.Special); Add(Instruction.FSIEnd, InstType.Special); - // TODO: LSB and MSB Implementations https://github.com/KhronosGroup/SPIRV-Cross/blob/bccaa94db814af33d8ef05c153e7c34d8bd4d685/reference/shaders-msl-no-opt/asm/comp/bitscan.asm.comp#L8 - Add(Instruction.FindLSB, InstType.Special); - Add(Instruction.FindMSBS32, InstType.Special); - Add(Instruction.FindMSBU32, InstType.Special); + Add(Instruction.FindLSB, InstType.CallUnary, HelperFunctionNames.FindLSB); + Add(Instruction.FindMSBS32, InstType.CallUnary, HelperFunctionNames.FindMSBS32); + Add(Instruction.FindMSBU32, InstType.CallUnary, HelperFunctionNames.FindMSBU32); Add(Instruction.Floor, InstType.CallUnary, "floor"); Add(Instruction.FusedMultiplyAdd, InstType.CallTernary, "fma"); Add(Instruction.GroupMemoryBarrier, InstType.Special); @@ -117,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); Add(Instruction.Store, InstType.Special); Add(Instruction.Subtract, InstType.OpBinary, "-", 2); - Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); + Add(Instruction.SwizzleAdd, InstType.Special); Add(Instruction.TextureSample, InstType.Special); Add(Instruction.TextureQuerySamples, InstType.Special); Add(Instruction.TextureQuerySize, InstType.Special); diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 4811b69de..7a3180788 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -14,8 +14,10 @@ - + - - + + + + diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs index 2a3d65e75..8e7bbd6f1 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs @@ -7,6 +7,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { MultiplyHighS32 = 1 << 2, MultiplyHighU32 = 1 << 3, + + FindLSB = 1 << 5, + FindMSBS32 = 1 << 6, + FindMSBU32 = 1 << 7, + SwizzleAdd = 1 << 10, FSI = 1 << 11, } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 88053658d..394099902 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -321,8 +321,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } // Those instructions needs to be emulated by using helper functions, - // because they are NVIDIA specific. Those flags helps the backend to - // decide which helper functions are needed on the final generated code. + // because they are NVIDIA specific or because the target language has + // no direct equivalent. Those flags helps the backend to decide which + // helper functions are needed on the final generated code. switch (operation.Inst) { case Instruction.MultiplyHighS32: @@ -331,6 +332,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr case Instruction.MultiplyHighU32: context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighU32; break; + case Instruction.FindLSB: + context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindLSB; + break; + case Instruction.FindMSBS32: + context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindMSBS32; + break; + case Instruction.FindMSBU32: + context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindMSBU32; + break; case Instruction.SwizzleAdd: context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd; break; -- 2.47.1 From dae0f3cdedf2054702ebd801df46589dea5184ad Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:25:31 +0100 Subject: [PATCH 248/368] Argument Buffers (#24) * Stuff * More arg buffer stuff * Fixes * Rebase * Pass storage buffers to inline functions * Fix binding * Fix typo + Fix a couple shaders * Enforce ids * Dispose * Mark used buffers as resident * Update depth clear shader * Fix non-contiguous struct defs * Update ChangeBufferStride * Fix StorageBuffer assignments * Fix odyssey crash * Retain buffer bindings * Pad Std140 * Set texture data with safe buffers * Clone buffers * Always declare vert in * Stop clears from breaking OpenGL games * Fix depth clear * Use invariant position * Horribly inefficient texture & sampler arg buffers * Fix missing struct access * Minimise rebinds as much as possible * Build arg buffers on staging buffer --- src/Ryujinx.Graphics.Metal/Constants.cs | 10 +- src/Ryujinx.Graphics.Metal/EncoderState.cs | 61 +- .../EncoderStateManager.cs | 519 ++++++++++-------- src/Ryujinx.Graphics.Metal/HelperShader.cs | 16 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 11 +- src/Ryujinx.Graphics.Metal/Sampler.cs | 3 +- src/Ryujinx.Graphics.Metal/Shaders/Blit.metal | 151 ++++- .../Shaders/ChangeBufferStride.metal | 39 +- .../Shaders/ColorClear.metal | 12 +- .../Shaders/DepthStencilClear.metal | 12 +- src/Ryujinx.Graphics.Metal/Texture.cs | 89 ++- .../CodeGen/Msl/CodeGenContext.cs | 2 +- .../CodeGen/Msl/Declarations.cs | 106 +++- .../Msl/{DefaultNames.cs => Defaults.cs} | 10 +- .../CodeGen/Msl/Instructions/InstGen.cs | 3 +- .../CodeGen/Msl/Instructions/InstGenCall.cs | 6 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 22 +- .../CodeGen/Msl/Instructions/IoMap.cs | 4 +- .../CodeGen/Msl/MslGenerator.cs | 41 +- .../CodeGen/Msl/OperandManager.cs | 6 +- 20 files changed, 721 insertions(+), 402 deletions(-) rename src/Ryujinx.Graphics.Shader/CodeGen/Msl/{DefaultNames.cs => Defaults.cs} (57%) diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index e1f858a84..032815359 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -4,19 +4,23 @@ namespace Ryujinx.Graphics.Metal { // TODO: Check these values, these were largely copied from Vulkan public const int MaxShaderStages = 5; + public const int MaxVertexBuffers = 16; public const int MaxUniformBuffersPerStage = 18; public const int MaxStorageBuffersPerStage = 16; public const int MaxTexturesPerStage = 64; - public const int MaxCommandBuffersPerQueue = 16; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; public const int MaxColorAttachments = 8; // TODO: Check this value public const int MaxVertexAttributes = 31; // TODO: Check this value public const int MaxVertexLayouts = 31; - public const int MaxTextures = 31; - public const int MaxSamplers = 16; public const int MinResourceAlignment = 16; + + // Must match constants set in shader generation + public const uint ConstantBuffersIndex = 20; + public const uint StorageBuffersIndex = 21; + public const uint ZeroBufferIndex = 18; + public const uint TexturesIndex = 22; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 72422c70d..1ba7e2620 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,42 +1,49 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; +using System; using System.Linq; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - struct DirtyFlags + [Flags] + enum DirtyFlags { - public bool RenderPipeline = false; - public bool ComputePipeline = false; - public bool DepthStencil = false; + None = 0, + RenderPipeline = 1 << 0, + ComputePipeline = 1 << 1, + DepthStencil = 1 << 2, + DepthClamp = 1 << 3, + DepthBias = 1 << 4, + CullMode = 1 << 5, + FrontFace = 1 << 6, + StencilRef = 1 << 7, + Viewports = 1 << 8, + Scissors = 1 << 9, + VertexBuffers = 1 << 10, + Buffers = 1 << 11, + VertexTextures = 1 << 12, + FragmentTextures = 1 << 13, + ComputeTextures = 1 << 14, - public DirtyFlags() { } - - public void MarkAll() - { - RenderPipeline = true; - ComputePipeline = true; - DepthStencil = true; - } + RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | VertexBuffers | Buffers | VertexTextures | FragmentTextures, + ComputeAll = ComputePipeline | Buffers | ComputeTextures, + All = RenderAll | ComputeAll, } record struct BufferRef { public Auto Buffer; - public int Index; public BufferRange? Range; - public BufferRef(Auto buffer, int index) + public BufferRef(Auto buffer) { Buffer = buffer; - Index = index; } - public BufferRef(Auto buffer, int index, ref BufferRange range) + public BufferRef(Auto buffer, ref BufferRange range) { Buffer = buffer; - Index = index; Range = range; } } @@ -48,17 +55,17 @@ namespace Ryujinx.Graphics.Metal public MTLFunction? FragmentFunction = null; public MTLFunction? ComputeFunction = null; - public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTextures]; - public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxSamplers]; + public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage]; + public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - public TextureBase[] VertexTextures = new TextureBase[Constants.MaxTextures]; - public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxSamplers]; + public TextureBase[] VertexTextures = new TextureBase[Constants.MaxTexturesPerStage]; + public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTextures]; - public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxSamplers]; + public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTexturesPerStage]; + public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - public BufferRef[] UniformBuffers = []; - public BufferRef[] StorageBuffers = []; + public BufferRef[] UniformBuffers = new BufferRef[Constants.MaxUniformBuffersPerStage]; + public BufferRef[] StorageBuffers = new BufferRef[Constants.MaxStorageBuffersPerStage]; public Auto IndexBuffer = default; public MTLIndexType IndexType = MTLIndexType.UInt16; @@ -99,7 +106,7 @@ namespace Ryujinx.Graphics.Metal public VertexAttribDescriptor[] VertexAttribs = []; // Dirty flags - public DirtyFlags Dirty = new(); + public DirtyFlags Dirty = DirtyFlags.None; // Only to be used for present public bool ClearLoadAction = false; @@ -119,6 +126,8 @@ namespace Ryujinx.Graphics.Metal clone.BlendDescriptors = (BlendDescriptor?[])BlendDescriptors.Clone(); clone.VertexBuffers = (VertexBufferDescriptor[])VertexBuffers.Clone(); clone.VertexAttribs = (VertexAttribDescriptor[])VertexAttribs.Clone(); + clone.UniformBuffers = (BufferRef[])UniformBuffers.Clone(); + clone.StorageBuffers = (BufferRef[])StorageBuffers.Clone(); return clone; } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 52d8df57c..218e378b0 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -79,21 +79,8 @@ namespace Ryujinx.Graphics.Metal { _currentState = _backStates.Pop(); - // Set all the inline state, since it might have changed - var renderCommandEncoder = _pipeline.GetOrCreateRenderEncoder(); - SetDepthClamp(renderCommandEncoder); - SetDepthBias(renderCommandEncoder); - SetScissors(renderCommandEncoder); - SetViewports(renderCommandEncoder); - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - SetCullMode(renderCommandEncoder); - SetFrontFace(renderCommandEncoder); - SetStencilRefValue(renderCommandEncoder); - // Mark the other state as dirty - _currentState.Dirty.MarkAll(); + _currentState.Dirty |= DirtyFlags.All; } else { @@ -165,29 +152,7 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); // Mark all state as dirty to ensure it is set on the encoder - _currentState.Dirty.MarkAll(); - - // Rebind all the state - SetDepthClamp(renderCommandEncoder); - SetDepthBias(renderCommandEncoder); - SetCullMode(renderCommandEncoder); - SetFrontFace(renderCommandEncoder); - SetStencilRefValue(renderCommandEncoder); - SetViewports(renderCommandEncoder); - SetScissors(renderCommandEncoder); - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - for (ulong i = 0; i < Constants.MaxTextures; i++) - { - SetRenderTexture(renderCommandEncoder, ShaderStage.Vertex, i, _currentState.VertexTextures[i]); - SetRenderTexture(renderCommandEncoder, ShaderStage.Fragment, i, _currentState.FragmentTextures[i]); - } - for (ulong i = 0; i < Constants.MaxSamplers; i++) - { - SetRenderSampler(renderCommandEncoder, ShaderStage.Vertex, i, _currentState.VertexSamplers[i]); - SetRenderSampler(renderCommandEncoder, ShaderStage.Fragment, i, _currentState.FragmentSamplers[i]); - } + _currentState.Dirty |= DirtyFlags.RenderAll; // Cleanup renderPassDescriptor.Dispose(); @@ -195,22 +160,13 @@ namespace Ryujinx.Graphics.Metal return renderCommandEncoder; } - public readonly MTLComputeCommandEncoder CreateComputeCommandEncoder() + public MTLComputeCommandEncoder CreateComputeCommandEncoder() { var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); - // Rebind all the state - SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers); - SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); - for (ulong i = 0; i < Constants.MaxTextures; i++) - { - SetComputeTexture(computeCommandEncoder, i, _currentState.ComputeTextures[i]); - } - for (ulong i = 0; i < Constants.MaxSamplers; i++) - { - SetComputeSampler(computeCommandEncoder, i, _currentState.ComputeSamplers[i]); - } + // Mark all state as dirty to ensure it is set on the encoder + _currentState.Dirty |= DirtyFlags.ComputeAll; // Cleanup descriptor.Dispose(); @@ -220,33 +176,93 @@ namespace Ryujinx.Graphics.Metal public void RebindRenderState(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.RenderPipeline) + if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline)) { SetRenderPipelineState(renderCommandEncoder); } - if (_currentState.Dirty.DepthStencil) + if (_currentState.Dirty.HasFlag(DirtyFlags.DepthStencil)) { SetDepthStencilState(renderCommandEncoder); } - // Clear the dirty flags - _currentState.Dirty.RenderPipeline = false; - _currentState.Dirty.DepthStencil = false; + if (_currentState.Dirty.HasFlag(DirtyFlags.DepthClamp)) + { + SetDepthClamp(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.DepthBias)) + { + SetDepthBias(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.CullMode)) + { + SetCullMode(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.FrontFace)) + { + SetFrontFace(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.StencilRef)) + { + SetStencilRefValue(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.Viewports)) + { + SetViewports(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.Scissors)) + { + SetScissors(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.VertexBuffers)) + { + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) + { + SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.VertexTextures)) + { + SetRenderTextures(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.FragmentTextures)) + { + SetRenderTextures(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + } + + _currentState.Dirty &= ~DirtyFlags.RenderAll; } public void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) { - if (_currentState.Dirty.ComputePipeline) + if (_currentState.Dirty.HasFlag(DirtyFlags.ComputePipeline)) { SetComputePipelineState(computeCommandEncoder); } - // Clear the dirty flags - _currentState.Dirty.ComputePipeline = false; + if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) + { + SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.ComputeTextures)) + { + SetComputeTextures(computeCommandEncoder, _currentState.ComputeTextures, _currentState.ComputeSamplers); + } } - private readonly void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) + private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); @@ -340,7 +356,7 @@ namespace Ryujinx.Graphics.Metal } } - private readonly void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) + private void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) { if (_currentState.ComputeFunction == null) { @@ -398,15 +414,13 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexFunction = prg.VertexFunction; _currentState.FragmentFunction = prg.FragmentFunction; - // Mark dirty - _currentState.Dirty.RenderPipeline = true; + _currentState.Dirty |= DirtyFlags.RenderPipeline; } if (prg.ComputeFunction != IntPtr.Zero) { _currentState.ComputeFunction = prg.ComputeFunction; - // Mark dirty - _currentState.Dirty.ComputePipeline = true; + _currentState.Dirty |= DirtyFlags.ComputePipeline; } } @@ -473,7 +487,7 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexAttribs = vertexAttribs.ToArray(); // Mark dirty - _currentState.Dirty.RenderPipeline = true; + _currentState.Dirty |= DirtyFlags.RenderPipeline; } public void UpdateBlendDescriptors(int index, BlendDescriptor blend) @@ -524,13 +538,12 @@ namespace Ryujinx.Graphics.Metal UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); // Mark dirty - _currentState.Dirty.DepthStencil = true; + _currentState.Dirty |= DirtyFlags.DepthStencil; // Cleanup descriptor.Dispose(); } - // Inlineable public void UpdateDepthState(DepthTestDescriptor depthTest) { _currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; @@ -551,7 +564,7 @@ namespace Ryujinx.Graphics.Metal _currentState.DepthStencilState = _depthStencilCache.GetOrCreate(descriptor); // Mark dirty - _currentState.Dirty.DepthStencil = true; + _currentState.Dirty |= DirtyFlags.DepthStencil; // Cleanup descriptor.Dispose(); @@ -567,7 +580,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetDepthClamp(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.DepthClamp; } // Inlineable @@ -582,7 +599,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetDepthBias(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.DepthBias; } // Inlineable @@ -610,7 +631,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetScissors(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.Scissors; } // Inlineable @@ -643,7 +668,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetViewports(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.Viewports; } public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) @@ -655,20 +684,17 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); + return; } // Mark dirty - _currentState.Dirty.RenderPipeline = true; + _currentState.Dirty |= DirtyFlags.RenderPipeline | DirtyFlags.VertexBuffers; } - // Inlineable public void UpdateUniformBuffers(ReadOnlySpan buffers) { - _currentState.UniformBuffers = new BufferRef[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) + foreach (BufferAssignment assignment in buffers) { - var assignment = buffers[i]; var buffer = assignment.Range; int index = assignment.Binding; @@ -676,87 +702,40 @@ namespace Ryujinx.Graphics.Metal ? null : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - _currentState.UniformBuffers[i] = new BufferRef(mtlBuffer, index, ref buffer); + _currentState.UniformBuffers[index] = new BufferRef(mtlBuffer, ref buffer); } - // Inline update - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers); - } - } + _currentState.Dirty |= DirtyFlags.Buffers; } - // Inlineable public void UpdateStorageBuffers(ReadOnlySpan buffers) { - _currentState.StorageBuffers = new BufferRef[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) + foreach (BufferAssignment assignment in buffers) { - var assignment = buffers[i]; var buffer = assignment.Range; - // TODO: Dont do this - int index = assignment.Binding + 15; + int index = assignment.Binding; Auto mtlBuffer = buffer.Handle == BufferHandle.Null ? null : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - _currentState.StorageBuffers[i] = new BufferRef(mtlBuffer, index, ref buffer); + _currentState.StorageBuffers[index] = new BufferRef(mtlBuffer, ref buffer); } - // Inline update - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); - } - } + _currentState.Dirty |= DirtyFlags.Buffers; } - // Inlineable public void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) { - _currentState.StorageBuffers = new BufferRef[buffers.Length]; - for (int i = 0; i < buffers.Length; i++) { var mtlBuffer = buffers[i]; int index = first + i; - _currentState.StorageBuffers[i] = new BufferRef(mtlBuffer, index); + _currentState.StorageBuffers[index] = new BufferRef(mtlBuffer); } - // Inline update - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); - } - } + _currentState.Dirty |= DirtyFlags.Buffers; } // Inlineable @@ -769,7 +748,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetCullMode(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.CullMode; } // Inlineable @@ -782,7 +765,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetFrontFace(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.FrontFace; } private void UpdateStencilRefValue(int frontRef, int backRef) @@ -796,84 +783,60 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetStencilRefValue(renderCommandEncoder); } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.StencilRef; } - // Inlineable - public readonly void UpdateTexture(ShaderStage stage, ulong binding, TextureBase texture) + public void UpdateTexture(ShaderStage stage, ulong binding, TextureBase texture) { - if (binding > 30) + if (binding > Constants.MaxTexturesPerStage) { - Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= 30"); + Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= {Constants.MaxTexturesPerStage}"); return; } switch (stage) { case ShaderStage.Fragment: _currentState.FragmentTextures[binding] = texture; + _currentState.Dirty |= DirtyFlags.FragmentTextures; break; case ShaderStage.Vertex: _currentState.VertexTextures[binding] = texture; + _currentState.Dirty |= DirtyFlags.VertexTextures; break; case ShaderStage.Compute: _currentState.ComputeTextures[binding] = texture; + _currentState.Dirty |= DirtyFlags.ComputeTextures; break; } - - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderTexture(renderCommandEncoder, ShaderStage.Vertex, binding, texture); - SetRenderTexture(renderCommandEncoder, ShaderStage.Fragment, binding, texture); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeTexture(computeCommandEncoder, binding, texture); - } - } } - // Inlineable - public readonly void UpdateSampler(ShaderStage stage, ulong binding, MTLSamplerState sampler) + public void UpdateSampler(ShaderStage stage, ulong binding, MTLSamplerState sampler) { - if (binding > 15) + if (binding > Constants.MaxTexturesPerStage) { - Logger.Warning?.Print(LogClass.Gpu, $"Sampler binding ({binding}) must be <= 15"); + Logger.Warning?.Print(LogClass.Gpu, $"Sampler binding ({binding}) must be <= {Constants.MaxTexturesPerStage}"); return; } switch (stage) { case ShaderStage.Fragment: _currentState.FragmentSamplers[binding] = sampler; + _currentState.Dirty |= DirtyFlags.FragmentTextures; break; case ShaderStage.Vertex: _currentState.VertexSamplers[binding] = sampler; + _currentState.Dirty |= DirtyFlags.VertexTextures; break; case ShaderStage.Compute: _currentState.ComputeSamplers[binding] = sampler; + _currentState.Dirty |= DirtyFlags.ComputeTextures; break; } - - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderSampler(renderCommandEncoder, ShaderStage.Vertex, binding, sampler); - SetRenderSampler(renderCommandEncoder, ShaderStage.Fragment, binding, sampler); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeSampler(computeCommandEncoder, binding, sampler); - } - } } - // Inlineable - public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, MTLSamplerState sampler) + public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, MTLSamplerState sampler) { UpdateTexture(stage, binding, texture); UpdateSampler(stage, binding, sampler); @@ -930,8 +893,8 @@ namespace Ryujinx.Graphics.Metal { var attrib = vertexDescriptor.Attributes.Object((ulong)i); attrib.Format = attribDescriptors[i].Format.Convert(); - indexMask |= 1u << bufferDescriptors.Length; - attrib.BufferIndex = (ulong)bufferDescriptors.Length; + indexMask |= 1u << (int)Constants.ZeroBufferIndex; + attrib.BufferIndex = Constants.ZeroBufferIndex; attrib.Offset = 0; } else @@ -979,9 +942,9 @@ namespace Ryujinx.Graphics.Metal } // Zero buffer - if ((indexMask & (1u << bufferDescriptors.Length)) != 0) + if ((indexMask & (1u << (int)Constants.ZeroBufferIndex)) != 0) { - var layout = vertexDescriptor.Layouts.Object((ulong)bufferDescriptors.Length); + var layout = vertexDescriptor.Layouts.Object(Constants.ZeroBufferIndex); layout.Stride = 1; layout.StepFunction = MTLVertexStepFunction.Constant; layout.StepRate = 0; @@ -992,39 +955,77 @@ namespace Ryujinx.Graphics.Metal private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) { - var buffers = new List(); - for (int i = 0; i < bufferDescriptors.Length; i++) { - Auto mtlBuffer = bufferDescriptors[i].Buffer.Handle == BufferHandle.Null + Auto autoBuffer = bufferDescriptors[i].Buffer.Handle == BufferHandle.Null ? null : _bufferManager.GetBuffer(bufferDescriptors[i].Buffer.Handle, bufferDescriptors[i].Buffer.Write); var range = bufferDescriptors[i].Buffer; + var offset = range.Offset; - buffers.Add(new BufferRef(mtlBuffer, i, ref range)); + if (autoBuffer == null) + { + continue; + } + + var mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Size, range.Write).Value; + renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)i); } - var zeroBufferRange = new BufferRange(_zeroBuffer, 0, ZeroBufferSize); - - Auto zeroBuffer = _zeroBuffer == BufferHandle.Null + Auto autoZeroBuffer = _zeroBuffer == BufferHandle.Null ? null : _bufferManager.GetBuffer(_zeroBuffer, false); - // Zero buffer - buffers.Add(new BufferRef(zeroBuffer, bufferDescriptors.Length, ref zeroBufferRange)); + if (autoZeroBuffer == null) + { + return; + } - SetRenderBuffers(renderCommandEncoder, buffers.ToArray()); + var zeroMtlBuffer = autoZeroBuffer.Get(_pipeline.Cbs).Value; + renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex); } - private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] buffers, bool fragment = false) + private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers) { + var uniformArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, uniformBuffers, true); + var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value; + + renderCommandEncoder.SetVertexBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); + renderCommandEncoder.SetFragmentBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); + + var storageArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, storageBuffers, false); + var storageArgBuffer = _bufferManager.GetBuffer(storageArgBufferRange.Handle, true).Get(_pipeline.Cbs).Value; + + renderCommandEncoder.SetVertexBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); + renderCommandEncoder.SetFragmentBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); + } + + private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers) + { + var uniformArgBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, uniformBuffers, true); + var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value; + + computeCommandEncoder.SetBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); + + + var storageArgBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, storageBuffers, false); + var storageArgBuffer = _bufferManager.GetBuffer(storageArgBufferRange.Handle, true).Get(_pipeline.Cbs).Value; + + computeCommandEncoder.SetBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); + } + + private readonly BufferRange CreateArgumentBufferForRenderEncoder(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] buffers, bool constant) + { + var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; + + ulong[] resourceIds = new ulong[buffers.Length]; + for (int i = 0; i < buffers.Length; i++) { var range = buffers[i].Range; var autoBuffer = buffers[i].Buffer; var offset = 0; - var index = buffers[i].Index; if (autoBuffer == null) { @@ -1044,23 +1045,29 @@ namespace Ryujinx.Graphics.Metal mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; } - renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)index); - - if (fragment) - { - renderCommandEncoder.SetFragmentBuffer(mtlBuffer, (ulong)offset, (ulong)index); - } + renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage, MTLRenderStages.RenderStageFragment | MTLRenderStages.RenderStageVertex); + resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset; } + + var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; + + var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + + return argBuffer.Range; } - private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] buffers) + private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] buffers, bool constant) { + var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; + + ulong[] resourceIds = new ulong[buffers.Length]; + for (int i = 0; i < buffers.Length; i++) { var range = buffers[i].Range; var autoBuffer = buffers[i].Buffer; var offset = 0; - var index = buffers[i].Index; if (autoBuffer == null) { @@ -1080,8 +1087,16 @@ namespace Ryujinx.Graphics.Metal mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; } - computeCommandEncoder.SetBuffer(mtlBuffer, (ulong)offset, (ulong)index); + computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage); + resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset; } + + var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; + + var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + + return argBuffer.Range; } private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) @@ -1099,64 +1114,104 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue); } - private static void SetRenderTexture(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, ulong binding, TextureBase texture) + private readonly void SetRenderTextures(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers) { - if (texture == null) - { - return; - } + var argBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, stage, textures, samplers); + var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value; - var textureHandle = texture.GetHandle(); - if (textureHandle != IntPtr.Zero) + switch (stage) { - switch (stage) + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); + break; + } + } + + private readonly void SetComputeTextures(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers) + { + var argBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, textures, samplers); + var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value; + + computeCommandEncoder.SetBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); + } + + private readonly BufferRange CreateArgumentBufferForRenderEncoder(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers) + { + var renderStage = stage == ShaderStage.Vertex ? MTLRenderStages.RenderStageVertex : MTLRenderStages.RenderStageFragment; + + ulong[] resourceIds = new ulong[textures.Length + samplers.Length]; + + for (int i = 0; i < textures.Length; i++) + { + if (textures[i] == null) { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexTexture(textureHandle, binding); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentTexture(textureHandle, binding); - break; + continue; } - } - } - private static void SetRenderSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, ulong binding, MTLSamplerState sampler) - { - if (sampler != IntPtr.Zero) + var mtlTexture = textures[i].GetHandle(); + + renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStage); + resourceIds[i] = mtlTexture.GpuResourceID._impl; + } + + for (int i = 0; i < samplers.Length; i++) { - switch (stage) + if (samplers[i].NativePtr == IntPtr.Zero) { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexSamplerState(sampler, binding); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentSamplerState(sampler, binding); - break; + continue; } + + var sampler = samplers[i]; + + resourceIds[i + textures.Length] = sampler.GpuResourceID._impl; } + + var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length); + + var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + + return argBuffer.Range; } - private static void SetComputeTexture(MTLComputeCommandEncoder computeCommandEncoder, ulong binding, TextureBase texture) + private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers) { - if (texture == null) + ulong[] resourceIds = new ulong[textures.Length + samplers.Length]; + + for (int i = 0; i < textures.Length; i++) { - return; + if (textures[i] == null) + { + continue; + } + + var mtlTexture = textures[i].GetHandle(); + + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); + resourceIds[i] = mtlTexture.GpuResourceID._impl; } - var textureHandle = texture.GetHandle(); - if (textureHandle != IntPtr.Zero) + for (int i = 0; i < samplers.Length; i++) { - computeCommandEncoder.SetTexture(textureHandle, binding); - } - } + if (samplers[i].NativePtr == IntPtr.Zero) + { + continue; + } - private static void SetComputeSampler(MTLComputeCommandEncoder computeCommandEncoder, ulong binding, MTLSamplerState sampler) - { - if (sampler != IntPtr.Zero) - { - computeCommandEncoder.SetSamplerState(sampler, binding); + var sampler = samplers[i]; + + resourceIds[i + textures.Length] = sampler.GpuResourceID._impl; } + + var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length); + + var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + + return argBuffer.Range; } } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index dcb5a4f62..6f953a583 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -77,7 +77,8 @@ namespace Ryujinx.Graphics.Metal ITexture dst, Extents2D srcRegion, Extents2D dstRegion, - bool linearFilter) + bool linearFilter, + bool clear = false) { // Save current state _pipeline.SaveAndResetState(); @@ -134,7 +135,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetRenderTargets([dst], null); _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dstWidth, dstHeight) }); - _pipeline.SetClearLoadAction(true); + _pipeline.SetClearLoadAction(clear); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); @@ -305,13 +306,15 @@ namespace Ryujinx.Graphics.Metal int dstWidth, int dstHeight) { - const int ClearDepthBufferSize = 4; - - IntPtr ptr = new(&depthValue); - // Save current state _pipeline.SaveState(); + const int ClearDepthBufferSize = 4; + + using var buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearDepthBufferSize); + buffer.Holder.SetDataUnchecked(buffer.Offset, new ReadOnlySpan(ref depthValue)); + _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); + Span viewports = stackalloc Viewport[1]; viewports[0] = new Viewport( @@ -330,7 +333,6 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetViewports(viewports); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); - _pipeline.GetOrCreateRenderEncoder(true).SetFragmentBytes(ptr, ClearDepthBufferSize, 0); _pipeline.Draw(4, 1, 0, 0); // Restore previous state diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index dd4a15c6d..93064e60a 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Metal return BeginBlitPass(); } - public MTLComputeCommandEncoder GetOrCreateComputeEncoder() + public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false) { MTLComputeCommandEncoder computeCommandEncoder; if (CurrentEncoder == null || CurrentEncoderType != EncoderType.Compute) @@ -116,7 +116,10 @@ namespace Ryujinx.Graphics.Metal computeCommandEncoder = new MTLComputeCommandEncoder(CurrentEncoder.Value); } - _encoderStateManager.RebindComputeState(computeCommandEncoder); + if (forDispatch) + { + _encoderStateManager.RebindComputeState(computeCommandEncoder); + } return computeCommandEncoder; } @@ -190,7 +193,7 @@ namespace Ryujinx.Graphics.Metal var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); var dst = new Texture(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0); - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear); + _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear, true); EndCurrentPass(); @@ -348,7 +351,7 @@ namespace Ryujinx.Graphics.Metal public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) { - var computeCommandEncoder = GetOrCreateComputeEncoder(); + var computeCommandEncoder = GetOrCreateComputeEncoder(true); computeCommandEncoder.DispatchThreadgroups( new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ }, diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index f416b5da5..9f8ae74b4 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -27,7 +27,8 @@ namespace Ryujinx.Graphics.Metal MaxAnisotropy = Math.Max((uint)info.MaxAnisotropy, 1), SAddressMode = info.AddressU.Convert(), TAddressMode = info.AddressV.Convert(), - RAddressMode = info.AddressP.Convert() + RAddressMode = info.AddressP.Convert(), + SupportArgumentBuffers = true }); _mtlSamplerState = samplerState; diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index 3d86a27a8..3c40af737 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -7,14 +7,154 @@ struct CopyVertexOut { float2 uv; }; +struct TexCoords { + float data[4]; +}; + +struct ConstantBuffers { + constant TexCoords* texCoord; +}; + +struct Textures +{ + texture2d texture; + ulong padding_1; + ulong padding_2; + ulong padding_3; + ulong padding_4; + ulong padding_5; + ulong padding_6; + ulong padding_7; + ulong padding_8; + ulong padding_9; + ulong padding_10; + ulong padding_11; + ulong padding_12; + ulong padding_13; + ulong padding_14; + ulong padding_15; + ulong padding_16; + ulong padding_17; + ulong padding_18; + ulong padding_19; + ulong padding_20; + ulong padding_21; + ulong padding_22; + ulong padding_23; + ulong padding_24; + ulong padding_25; + ulong padding_26; + ulong padding_27; + ulong padding_28; + ulong padding_29; + ulong padding_30; + ulong padding_31; + ulong padding_32; + ulong padding_33; + ulong padding_34; + ulong padding_35; + ulong padding_36; + ulong padding_37; + ulong padding_38; + ulong padding_39; + ulong padding_40; + ulong padding_41; + ulong padding_42; + ulong padding_43; + ulong padding_44; + ulong padding_45; + ulong padding_46; + ulong padding_47; + ulong padding_48; + ulong padding_49; + ulong padding_50; + ulong padding_51; + ulong padding_52; + ulong padding_53; + ulong padding_54; + ulong padding_55; + ulong padding_56; + ulong padding_57; + ulong padding_58; + ulong padding_59; + ulong padding_60; + ulong padding_61; + ulong padding_62; + ulong padding_63; + sampler sampler; + ulong padding_65; + ulong padding_66; + ulong padding_67; + ulong padding_68; + ulong padding_69; + ulong padding_70; + ulong padding_71; + ulong padding_72; + ulong padding_73; + ulong padding_74; + ulong padding_75; + ulong padding_76; + ulong padding_77; + ulong padding_78; + ulong padding_79; + ulong padding_80; + ulong padding_81; + ulong padding_82; + ulong padding_83; + ulong padding_84; + ulong padding_85; + ulong padding_86; + ulong padding_87; + ulong padding_88; + ulong padding_89; + ulong padding_90; + ulong padding_91; + ulong padding_92; + ulong padding_93; + ulong padding_94; + ulong padding_95; + ulong padding_96; + ulong padding_97; + ulong padding_98; + ulong padding_99; + ulong padding_100; + ulong padding_101; + ulong padding_102; + ulong padding_103; + ulong padding_104; + ulong padding_105; + ulong padding_106; + ulong padding_107; + ulong padding_108; + ulong padding_109; + ulong padding_110; + ulong padding_111; + ulong padding_112; + ulong padding_113; + ulong padding_114; + ulong padding_115; + ulong padding_116; + ulong padding_117; + ulong padding_118; + ulong padding_119; + ulong padding_120; + ulong padding_121; + ulong padding_122; + ulong padding_123; + ulong padding_124; + ulong padding_125; + ulong padding_126; + ulong padding_127; +}; + vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], - const device float* texCoord [[buffer(0)]]) { + constant ConstantBuffers &constant_buffers [[buffer(20)]]) { CopyVertexOut out; int low = vid & 1; int high = vid >> 1; - out.uv.x = texCoord[low]; - out.uv.y = texCoord[2 + high]; + out.uv.x = constant_buffers.texCoord->data[low]; + out.uv.y = constant_buffers.texCoord->data[2 + high]; out.position.x = (float(low) - 0.5f) * 2.0f; out.position.y = (float(high) - 0.5f) * 2.0f; out.position.z = 0.0f; @@ -24,7 +164,6 @@ vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], } fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], - texture2d texture [[texture(0)]], - sampler sampler [[sampler(0)]]) { - return texture.sample(sampler, in.uv); + constant Textures &textures [[buffer(22)]]) { + return textures.texture.sample(textures.sampler, in.uv); } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal index 64e832092..38eedefb7 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal @@ -2,19 +2,40 @@ using namespace metal; -kernel void kernelMain(constant int4& stride_arguments [[buffer(0)]], - device uint8_t* in_data [[buffer(1)]], - device uint8_t* out_data [[buffer(2)]], +struct StrideArguments { + int4 data; +}; + +struct InData { + uint8_t data[1]; +}; + +struct OutData { + uint8_t data[1]; +}; + +struct ConstantBuffers { + constant StrideArguments* stride_arguments; +}; + +struct StorageBuffers { + ulong padding; + device InData* in_data; + device OutData* out_data; +}; + +kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(20)]], + device StorageBuffers &storage_buffers [[buffer(21)]], uint3 thread_position_in_grid [[thread_position_in_grid]], uint3 threads_per_threadgroup [[threads_per_threadgroup]], uint3 threadgroups_per_grid [[threads_per_grid]]) { // Determine what slice of the stride copies this invocation will perform. - int sourceStride = stride_arguments.x; - int targetStride = stride_arguments.y; - int bufferSize = stride_arguments.z; - int sourceOffset = stride_arguments.w; + int sourceStride = constant_buffers.stride_arguments->data.x; + int targetStride = constant_buffers.stride_arguments->data.y; + int bufferSize = constant_buffers.stride_arguments->data.z; + int sourceOffset = constant_buffers.stride_arguments->data.w; int strideRemainder = targetStride - sourceStride; int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); @@ -42,11 +63,11 @@ kernel void kernelMain(constant int4& stride_arguments [[buffer(0)]], // Perform the copies for this region for (int i = 0; i < copyCount; i++) { for (int j = 0; j < sourceStride; j++) { - out_data[dstOffset++] = in_data[srcOffset++]; + storage_buffers.out_data->data[dstOffset++] = storage_buffers.in_data->data[srcOffset++]; } for (int j = 0; j < strideRemainder; j++) { - out_data[dstOffset++] = uint8_t(0); + storage_buffers.out_data->data[dstOffset++] = uint8_t(0); } } } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal index 087c48606..d3ef9603f 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal @@ -6,6 +6,14 @@ struct VertexOut { float4 position [[position]]; }; +struct ClearColor { + float4 data; +}; + +struct ConstantBuffers { + constant ClearColor* clear_color; +}; + vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { int low = vid & 1; int high = vid >> 1; @@ -25,6 +33,6 @@ struct FragmentOut { }; fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant float4& clear_color [[buffer(0)]]) { - return {clear_color}; + constant ConstantBuffers &constant_buffers [[buffer(20)]]) { + return {constant_buffers.clear_color->data}; } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal index 019bf78d4..0fb3bd858 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal @@ -11,6 +11,14 @@ struct FragmentOut { uint stencil [[stencil]]; }; +struct ClearDepth { + float data; +}; + +struct ConstantBuffers { + constant ClearDepth* clear_depth; +}; + vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { int low = vid & 1; int high = vid >> 1; @@ -26,10 +34,10 @@ vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { } fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant float& clear_depth [[buffer(0)]]) { + constant ConstantBuffers &constant_buffers [[buffer(20)]]) { FragmentOut out; - out.depth = clear_depth; + out.depth = constant_buffers.clear_depth->data; // out.stencil = stencil_clear; return out; diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 656e67811..57e446ce6 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Metal levels.length = (ulong)Info.Levels; NSRange slices; slices.location = (ulong)firstLayer; - slices.length = (ulong)info.GetDepthOrLayers(); + slices.length = textureType == MTLTextureType.Type3D ? 1 : (ulong)info.GetDepthOrLayers(); var swizzle = GetSwizzle(info, pixelFormat); @@ -287,14 +287,15 @@ namespace Ryujinx.Graphics.Metal } } - public unsafe void SetData(IMemoryOwner data) + public void SetData(IMemoryOwner data) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); var dataSpan = data.Memory.Span; - var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); - var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); - dataSpan.CopyTo(bufferSpan); + + var buffer = _renderer.BufferManager.Create(dataSpan.Length); + buffer.SetDataUnchecked(0, dataSpan); + var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; int width = Info.Width; int height = Info.Height; @@ -342,7 +343,7 @@ namespace Ryujinx.Graphics.Metal } // Cleanup - mtlBuffer.Dispose(); + buffer.Dispose(); } public void SetData(IMemoryOwner data, int layer, int level) @@ -356,28 +357,26 @@ namespace Ryujinx.Graphics.Metal bytesPerImage = bytesPerRow * (ulong)Info.Height; } - unsafe - { - var dataSpan = data.Memory.Span; - var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); - var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); - dataSpan.CopyTo(bufferSpan); + var dataSpan = data.Memory.Span; - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage, - new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin() - ); + var buffer = _renderer.BufferManager.Create(dataSpan.Length); + buffer.SetDataUnchecked(0, dataSpan); + var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; - // Cleanup - mtlBuffer.Dispose(); - } + blitCommandEncoder.CopyFromBuffer( + mtlBuffer, + 0, + bytesPerRow, + bytesPerImage, + new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, + _mtlTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin() + ); + + // Cleanup + buffer.Dispose(); } public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) @@ -391,28 +390,26 @@ namespace Ryujinx.Graphics.Metal bytesPerImage = bytesPerRow * (ulong)Info.Height; } - unsafe - { - var dataSpan = data.Memory.Span; - var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); - var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); - dataSpan.CopyTo(bufferSpan); + var dataSpan = data.Memory.Span; - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage, - new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } - ); + var buffer = _renderer.BufferManager.Create(dataSpan.Length); + buffer.SetDataUnchecked(0, dataSpan); + var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; - // Cleanup - mtlBuffer.Dispose(); - } + blitCommandEncoder.CopyFromBuffer( + mtlBuffer, + 0, + bytesPerRow, + bytesPerImage, + new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, + _mtlTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } + ); + + // Cleanup + buffer.Dispose(); } public void SetStorage(BufferRange buffer) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index 0ae6313eb..79c13964c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string Tab = " "; // The number of additional arguments that every function (except for the main one) must have (for instance support_buffer) - public const int AdditionalArgCount = 1; + public const int AdditionalArgCount = 2; public StructuredFunction CurrentFunction { get; set; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index fc199da2c..59cc5c56b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -56,8 +56,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(); DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); context.AppendLine(); - DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values); - DeclareBufferStructures(context, context.Properties.StorageBuffers.Values); + DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values, true); + DeclareBufferStructures(context, context.Properties.StorageBuffers.Values, false); + DeclareTextures(context, context.Properties.Textures.Values); if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) { @@ -112,11 +113,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { string name = context.OperandManager.DeclareLocal(decl); - context.AppendLine(GetVarTypeName(context, decl.VarType) + " " + name + ";"); + context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); } } - public static string GetVarTypeName(CodeGenContext context, AggregateType type, bool atomic = false) + public static string GetVarTypeName(AggregateType type, bool atomic = false) { var s32 = atomic ? "atomic_int" : "int"; var u32 = atomic ? "atomic_uint" : "uint"; @@ -155,21 +156,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { arraySize = $"[{memory.ArrayLength}]"; } - var typeName = GetVarTypeName(context, memory.Type & ~AggregateType.Array); + var typeName = GetVarTypeName(memory.Type & ~AggregateType.Array); context.AppendLine($"{prefix}{typeName} {memory.Name}{arraySize};"); } } - private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers) + private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers, bool constant) { + var name = constant ? "ConstantBuffers" : "StorageBuffers"; + var count = constant ? Defaults.MaxUniformBuffersPerStage : Defaults.MaxStorageBuffersPerStage; + var addressSpace = constant ? "constant" : "device"; + + var argBufferPointers = new string[count]; + foreach (BufferDefinition buffer in buffers) { - context.AppendLine($"struct {DefaultNames.StructPrefix}_{buffer.Name}"); + var needsPadding = buffer.Layout == BufferLayout.Std140; + + argBufferPointers[buffer.Binding] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};"; + + context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); context.EnterScope(); foreach (StructureField field in buffer.Type.Fields) { - string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); + var type = field.Type; + type |= (needsPadding && (field.Type & AggregateType.Array) != 0) ? AggregateType.Vector4 : AggregateType.Invalid; + + type &= ~AggregateType.Array; + + string typeName = GetVarTypeName(type); string arraySuffix = ""; if (field.Type.HasFlag(AggregateType.Array)) @@ -191,6 +207,62 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.LeaveScope(";"); context.AppendLine(); } + + context.AppendLine($"struct {name}"); + context.EnterScope(); + + for (int i = 0; i < argBufferPointers.Length; i++) + { + if (argBufferPointers[i] == null) + { + // We need to pad the struct definition in order to read + // non-contiguous resources correctly. + context.AppendLine($"ulong padding_{i};"); + } + else + { + context.AppendLine(argBufferPointers[i]); + } + } + + context.LeaveScope(";"); + context.AppendLine(); + } + + private static void DeclareTextures(CodeGenContext context, IEnumerable textures) + { + context.AppendLine("struct Textures"); + context.EnterScope(); + + var argBufferPointers = new string[Defaults.MaxTexturesPerStage * 2]; + + foreach (TextureDefinition texture in textures) + { + var textureTypeName = texture.Type.ToMslTextureType(); + argBufferPointers[texture.Binding] = $"{textureTypeName} tex_{texture.Name};"; + + if (!texture.Separate) + { + argBufferPointers[Defaults.MaxTexturesPerStage + texture.Binding] = $"sampler samp_{texture.Name};"; + } + } + + for (int i = 0; i < argBufferPointers.Length; i++) + { + if (argBufferPointers[i] == null) + { + // We need to pad the struct definition in order to read + // non-contiguous resources correctly. + context.AppendLine($"ulong padding_{i};"); + } + else + { + context.AppendLine(argBufferPointers[i]); + } + } + + context.LeaveScope(";"); + context.AppendLine(); } private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) @@ -201,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else { - if (inputs.Any() || context.Definitions.Stage == ShaderStage.Fragment) + if (inputs.Any() || context.Definitions.Stage != ShaderStage.Compute) { string prefix = ""; @@ -220,7 +292,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl if (context.Definitions.Stage == ShaderStage.Fragment) { // TODO: check if it's needed - context.AppendLine("float4 position [[position]];"); + context.AppendLine("float4 position [[position, invariant]];"); context.AppendLine("bool front_facing [[front_facing]];"); } @@ -233,7 +305,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.VertexId => "uint", IoVariable.VertexIndex => "uint", IoVariable.PointCoord => "float2", - _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) + _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) }; string name = ioDefinition.IoVariable switch { @@ -242,11 +314,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.VertexId => "vertex_id", IoVariable.VertexIndex => "vertex_index", IoVariable.PointCoord => "point_coord", - _ => $"{DefaultNames.IAttributePrefix}{ioDefinition.Location}" + _ => $"{Defaults.IAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch { - // IoVariable.Position => "[[position]]", + // IoVariable.Position => "[[position, invariant]]", IoVariable.GlobalId => "[[thread_position_in_grid]]", IoVariable.VertexId => "[[vertex_id]]", // TODO: Avoid potential redeclaration @@ -297,9 +369,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { IoVariable.Position => "float4", IoVariable.PointSize => "float", - IoVariable.FragmentOutputColor => GetVarTypeName(context, context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), + IoVariable.FragmentOutputColor => GetVarTypeName(context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), IoVariable.FragmentOutputDepth => "float", - _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) + _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) }; string name = ioDefinition.IoVariable switch { @@ -307,11 +379,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.PointSize => "point_size", IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", IoVariable.FragmentOutputDepth => "depth", - _ => $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}" + _ => $"{Defaults.OAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch { - IoVariable.Position => "[[position]]", + IoVariable.Position => "[[position, invariant]]", IoVariable.PointSize => "[[point_size]]", IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs similarity index 57% rename from src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs index 0b946c3aa..c01242ffe 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { - static class DefaultNames + static class Defaults { public const string LocalNamePrefix = "temp"; @@ -13,5 +13,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string ArgumentNamePrefix = "a"; public const string UndefinedName = "0"; + + public const int MaxUniformBuffersPerStage = 18; + public const int MaxStorageBuffersPerStage = 16; + public const int MaxTexturesPerStage = 64; + + public const uint ConstantBuffersIndex = 20; + public const uint StorageBuffersIndex = 21; + public const uint TexturesIndex = 22; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 6c983445b..8d4ef0e37 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -49,8 +49,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions ? AggregateType.S32 : AggregateType.U32; - builder.Append($"(device {Declarations.GetVarTypeName(context, dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); - + builder.Append($"(device {Declarations.GetVarTypeName(dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs index c063ff458..0bad36f73 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs @@ -21,11 +21,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions if (context.Definitions.Stage != ShaderStage.Compute) { args[0] = "in"; - args[1] = "support_buffer"; + args[1] = "constant_buffers"; + args[2] = "storage_buffers"; } else { - args[0] = "support_buffer"; + args[0] = "constant_buffers"; + args[1] = "storage_buffers"; } int argIndex = additionalArgCount; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index bb1a69939..93eaee5dd 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions int srcIndex = 0; bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); int inputsCount = isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount; + bool fieldHasPadding = false; if (operation.Inst == Instruction.AtomicCompareAndSwap) { @@ -46,7 +47,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } StructureField field = buffer.Type.Fields[fieldIndex.Value]; - varName = buffer.Name; + + fieldHasPadding = buffer.Layout == BufferLayout.Std140 + && ((field.Type & AggregateType.Vector4) == 0) + && ((field.Type & AggregateType.Array) != 0); + + varName = storageKind == StorageKind.ConstantBuffer + ? "constant_buffers" + : "storage_buffers"; + varName += "." + buffer.Name; varName += "->" + field.Name; varType = field.Type; break; @@ -130,6 +139,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } } varName += fieldName; + varName += fieldHasPadding ? ".x" : ""; if (isStore) { @@ -173,7 +183,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions coordsExpr = GetSourceExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32); } - return $"tex_{samplerName}.calculate_unclamped_lod(samp_{samplerName}, {coordsExpr}){GetMaskMultiDest(texOp.Index)}"; + return $"textures.tex_{samplerName}.calculate_unclamped_lod(textures.samp_{samplerName}, {coordsExpr}){GetMaskMultiDest(texOp.Index)}"; } public static string Store(CodeGenContext context, AstOperation operation) @@ -199,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions bool colorIsVector = isGather || !isShadow; string samplerName = GetSamplerName(context.Properties, texOp); - string texCall = $"tex_{samplerName}"; + string texCall = $"textures.tex_{samplerName}"; texCall += "."; int srcIndex = 0; @@ -229,7 +239,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCall += "_compare"; } - texCall += $"(samp_{samplerName}, "; + texCall += $"(textures.samp_{samplerName}, "; } int coordsCount = texOp.Type.GetDimensions(); @@ -385,7 +395,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } string samplerName = GetSamplerName(context.Properties, texOp); - string textureName = $"tex_{samplerName}"; + string textureName = $"textures.tex_{samplerName}"; string texCall = textureName + "."; texCall += "get_num_samples()"; @@ -397,7 +407,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions AstTextureOperation texOp = (AstTextureOperation)operation; string samplerName = GetSamplerName(context.Properties, texOp); - string textureName = $"tex_{samplerName}"; + string textureName = $"textures.tex_{samplerName}"; string texCall = textureName + "."; if (texOp.Index == 3) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index f9d0a96d9..bb0f7f010 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -60,8 +60,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions private static (string, AggregateType) GetUserDefinedVariableName(ShaderDefinitions definitions, int location, int component, bool isOutput, bool isPerPatch) { string name = isPerPatch - ? DefaultNames.PerPatchAttributePrefix - : (isOutput ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix); + ? Defaults.PerPatchAttributePrefix + : (isOutput ? Defaults.OAttributePrefix : Defaults.IAttributePrefix); if (location < 0) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index a3e09d3cb..248b7159c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -73,18 +73,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl if (stage != ShaderStage.Compute) { args[0] = stage == ShaderStage.Vertex ? "VertexIn in" : "FragmentIn in"; - args[1] = $"constant {DefaultNames.StructPrefix}_support_buffer* support_buffer"; + args[1] = "constant ConstantBuffers &constant_buffers"; + args[2] = "device StorageBuffers &storage_buffers"; } else { - args[0] = $"constant {DefaultNames.StructPrefix}_support_buffer* support_buffer"; + args[0] = "constant ConstantBuffers &constant_buffers"; + args[1] = "device StorageBuffers &storage_buffers"; } } int argIndex = additionalArgCount; for (int i = 0; i < function.InArguments.Length; i++) { - args[argIndex++] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; + args[argIndex++] = $"{Declarations.GetVarTypeName(function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; } for (int i = 0; i < function.OutArguments.Length; i++) @@ -92,12 +94,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl int j = i + function.InArguments.Length; // Likely need to be made into pointers - args[argIndex++] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; + args[argIndex++] = $"out {Declarations.GetVarTypeName(function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; } string funcKeyword = "inline"; string funcName = null; - string returnType = Declarations.GetVarTypeName(context, function.ReturnType); + string returnType = Declarations.GetVarTypeName(function.ReturnType); if (isMainFunc) { @@ -122,10 +124,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl if (stage == ShaderStage.Vertex) { - if (context.AttributeUsage.UsedInputAttributes != 0) - { - args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); - } + args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); } else if (stage == ShaderStage.Fragment) { @@ -148,27 +147,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl args = args.Append("uint thread_index_in_simdgroup [[thread_index_in_simdgroup]]").ToArray(); } - foreach (var constantBuffer in context.Properties.ConstantBuffers.Values) - { - args = args.Append($"constant {DefaultNames.StructPrefix}_{constantBuffer.Name}* {constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); - } - - foreach (var storageBuffers in context.Properties.StorageBuffers.Values) - { - // Offset the binding by 15 to avoid clashing with the constant buffers - args = args.Append($"device {DefaultNames.StructPrefix}_{storageBuffers.Name}* {storageBuffers.Name} [[buffer({storageBuffers.Binding + 15})]]").ToArray(); - } - - foreach (var texture in context.Properties.Textures.Values) - { - var textureTypeName = texture.Type.ToMslTextureType(); - args = args.Append($"{textureTypeName} tex_{texture.Name} [[texture({texture.Binding})]]").ToArray(); - // If the texture is not separate, we need to declare a sampler - if (!texture.Separate) - { - args = args.Append($"sampler samp_{texture.Name} [[sampler({texture.Binding})]]").ToArray(); - } - } + args = args.Append($"constant ConstantBuffers &constant_buffers [[buffer({Defaults.ConstantBuffersIndex})]]").ToArray(); + args = args.Append($"device StorageBuffers &storage_buffers [[buffer({Defaults.StorageBuffersIndex})]]").ToArray(); + args = args.Append($"constant Textures &textures [[buffer({Defaults.TexturesIndex})]]").ToArray(); } var funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}("; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs index 6d211b7e8..e131a645e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public string DeclareLocal(AstOperand operand) { - string name = $"{DefaultNames.LocalNamePrefix}_{_locals.Count}"; + string name = $"{Defaults.LocalNamePrefix}_{_locals.Count}"; _locals.Add(operand, name); @@ -34,14 +34,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl OperandType.Argument => GetArgumentName(operand.Value), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.LocalVariable => _locals[operand], - OperandType.Undefined => DefaultNames.UndefinedName, + OperandType.Undefined => Defaults.UndefinedName, _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."), }; } public static string GetArgumentName(int argIndex) { - return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; + return $"{Defaults.ArgumentNamePrefix}{argIndex}"; } public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node) -- 2.47.1 From f1086afcdfe444f6a7f249e4cfd507de6af3b3e4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 25 Jun 2024 14:51:54 +0100 Subject: [PATCH 249/368] Fix fragment point_coord in --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 7 ++++--- .../CodeGen/Msl/Instructions/IoMap.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 59cc5c56b..b15b482db 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -294,6 +294,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl // TODO: check if it's needed context.AppendLine("float4 position [[position, invariant]];"); context.AppendLine("bool front_facing [[front_facing]];"); + context.AppendLine("float2 point_coord [[point_coord]];"); } foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) @@ -304,7 +305,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.GlobalId => "uint3", IoVariable.VertexId => "uint", IoVariable.VertexIndex => "uint", - IoVariable.PointCoord => "float2", + // IoVariable.PointCoord => "float2", _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) }; string name = ioDefinition.IoVariable switch @@ -313,7 +314,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.GlobalId => "global_id", IoVariable.VertexId => "vertex_id", IoVariable.VertexIndex => "vertex_index", - IoVariable.PointCoord => "point_coord", + // IoVariable.PointCoord => "point_coord", _ => $"{Defaults.IAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch @@ -323,7 +324,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.VertexId => "[[vertex_id]]", // TODO: Avoid potential redeclaration IoVariable.VertexIndex => "[[vertex_id]]", - IoVariable.PointCoord => "[[point_coord]]", + // IoVariable.PointCoord => "[[point_coord]]", IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]", _ => "" }; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index bb0f7f010..5db42bbef 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.InstanceId => ("instance_id", AggregateType.U32), IoVariable.InstanceIndex => ("instance_index", AggregateType.U32), IoVariable.InvocationId => ("INVOCATION_ID", AggregateType.S32), - IoVariable.PointCoord => ("point_coord", AggregateType.Vector2 | AggregateType.FP32), + IoVariable.PointCoord => ("in.point_coord", AggregateType.Vector2 | AggregateType.FP32), IoVariable.PointSize => ("out.point_size", AggregateType.FP32), IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), -- 2.47.1 From 30f194e5c03afcdc6d8755df6577373708f04640 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 12:39:05 +0100 Subject: [PATCH 250/368] Warning about host map buffer creation --- src/Ryujinx.Graphics.Metal/BufferManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs index 766c1dea2..76d6d4fb8 100644 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -63,6 +63,7 @@ namespace Ryujinx.Graphics.Metal public BufferHandle Create(nint pointer, int size) { + // TODO: This is the wrong Metal method, we need no-copy which SharpMetal isn't giving us. var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); if (buffer == IntPtr.Zero) -- 2.47.1 From 4f699ef96aabd78c06c155344da92fe65a889208 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 12:39:25 +0100 Subject: [PATCH 251/368] Fix Cull FrontAndBack --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 1 + .../EncoderStateManager.cs | 33 +++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 1ba7e2620..3d5c61edd 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -90,6 +90,7 @@ namespace Ryujinx.Graphics.Metal public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.CounterClockwise; + public bool CullBoth = false; public MTLViewport[] Viewports = []; public MTLScissorRect[] Scissors = []; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 218e378b0..b9e527d55 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] struct EncoderStateManager : IDisposable { + private readonly MTLDevice _device; private readonly Pipeline _pipeline; private readonly BufferManager _bufferManager; @@ -35,6 +36,7 @@ namespace Ryujinx.Graphics.Metal public unsafe EncoderStateManager(MTLDevice device, BufferManager bufferManager, Pipeline pipeline) { + _device = device; _pipeline = pipeline; _bufferManager = bufferManager; @@ -533,7 +535,7 @@ namespace Ryujinx.Graphics.Metal descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; } - _currentState.DepthStencilState = _depthStencilCache.GetOrCreate(descriptor); + _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); @@ -547,7 +549,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateDepthState(DepthTestDescriptor depthTest) { _currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; - _currentState.DepthWriteEnabled = depthTest.WriteEnable; + _currentState.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable; var descriptor = new MTLDepthStencilDescriptor { @@ -561,7 +563,7 @@ namespace Ryujinx.Graphics.Metal descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; } - _currentState.DepthStencilState = _depthStencilCache.GetOrCreate(descriptor); + _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); // Mark dirty _currentState.Dirty |= DirtyFlags.DepthStencil; @@ -741,18 +743,27 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateCullMode(bool enable, Face face) { + var dirtyScissor = (face == Face.FrontAndBack) != _currentState.CullBoth; + _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; + _currentState.CullBoth = face == Face.FrontAndBack; // Inline update if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetCullMode(renderCommandEncoder); + SetScissors(renderCommandEncoder); return; } // Mark dirty _currentState.Dirty |= DirtyFlags.CullMode; + + if (dirtyScissor) + { + _currentState.Dirty |= DirtyFlags.Scissors; + } } // Inlineable @@ -862,11 +873,21 @@ namespace Ryujinx.Graphics.Metal private unsafe void SetScissors(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Scissors.Length > 0) + var isTriangles = (_currentState.Topology == PrimitiveTopology.Triangles) || + (_currentState.Topology == PrimitiveTopology.TriangleStrip); + + if (_currentState.CullBoth && isTriangles) { - fixed (MTLScissorRect* pMtlScissors = _currentState.Scissors) + renderCommandEncoder.SetScissorRect(new MTLScissorRect { x = 0, y = 0, width = 0, height = 0}); + } + else + { + if (_currentState.Scissors.Length > 0) { - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_currentState.Scissors.Length); + fixed (MTLScissorRect* pMtlScissors = _currentState.Scissors) + { + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_currentState.Scissors.Length); + } } } } -- 2.47.1 From d6dcc391314f0be365a91b99feab53d073689f99 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 15:00:22 +0100 Subject: [PATCH 252/368] Enable Alpha Test workaround on Metal --- src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs | 8 ++++---- src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 1be75f242..4e9134291 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private readonly GpuAccessorState _state; private readonly int _stageIndex; private readonly bool _compute; - private readonly bool _isVulkan; + private readonly bool _isOpenGL; private readonly bool _hasGeometryShader; private readonly bool _supportsQuads; @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Shader _channel = channel; _state = state; _stageIndex = stageIndex; - _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; + _isOpenGL = context.Capabilities.Api == TargetApi.OpenGL; _hasGeometryShader = hasGeometryShader; _supportsQuads = context.Capabilities.SupportsQuads; @@ -116,10 +116,10 @@ namespace Ryujinx.Graphics.Gpu.Shader public GpuGraphicsState QueryGraphicsState() { return _state.GraphicsState.CreateShaderGraphicsState( - !_isVulkan, + _isOpenGL, _supportsQuads, _hasGeometryShader, - _isVulkan || _state.GraphicsState.YNegateEnabled); + !_isOpenGL || _state.GraphicsState.YNegateEnabled); } /// diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 93064e60a..6167d3127 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -428,7 +428,9 @@ namespace Ryujinx.Graphics.Metal public void SetAlphaTest(bool enable, float reference, CompareOp op) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // This is currently handled using shader specialization, as Metal does not support alpha test. + // In the future, we may want to use this to write the reference value into the support buffer, + // to avoid creating one version of the shader per reference value used. } public void SetBlendState(AdvancedBlendDescriptor blend) -- 2.47.1 From 1af7dc4b68f55405643a3a32a786c10dd3b2aabe Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 17:38:23 +0100 Subject: [PATCH 253/368] Fix stencil clears --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 3 +-- src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 6f953a583..1387a5a35 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -309,7 +309,7 @@ namespace Ryujinx.Graphics.Metal // Save current state _pipeline.SaveState(); - const int ClearDepthBufferSize = 4; + const int ClearDepthBufferSize = 16; using var buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearDepthBufferSize); buffer.Holder.SetDataUnchecked(buffer.Offset, new ReadOnlySpan(ref depthValue)); @@ -328,7 +328,6 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetProgram(_programDepthStencilClear); _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetViewports(viewports); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal index 0fb3bd858..4ee4f4a51 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal @@ -8,7 +8,6 @@ struct VertexOut { struct FragmentOut { float depth [[depth(any)]]; - uint stencil [[stencil]]; }; struct ClearDepth { @@ -38,7 +37,6 @@ fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], FragmentOut out; out.depth = constant_buffers.clear_depth->data; - // out.stencil = stencil_clear; return out; } -- 2.47.1 From a9633981a8cd36a0321a668b7ee171dcf72ab272 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 18:43:16 +0100 Subject: [PATCH 254/368] Be better about memory --- .../EncoderStateManager.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index b9e527d55..01d4247bc 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader; using SharpMetal.Metal; using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment; @@ -1040,7 +1041,7 @@ namespace Ryujinx.Graphics.Metal { var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; - ulong[] resourceIds = new ulong[buffers.Length]; + Span resourceIds = stackalloc ulong[buffers.Length]; for (int i = 0; i < buffers.Length; i++) { @@ -1073,7 +1074,7 @@ namespace Ryujinx.Graphics.Metal var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); return argBuffer.Range; } @@ -1082,7 +1083,7 @@ namespace Ryujinx.Graphics.Metal { var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; - ulong[] resourceIds = new ulong[buffers.Length]; + Span resourceIds = stackalloc ulong[buffers.Length]; for (int i = 0; i < buffers.Length; i++) { @@ -1115,7 +1116,7 @@ namespace Ryujinx.Graphics.Metal var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); return argBuffer.Range; } @@ -1163,7 +1164,7 @@ namespace Ryujinx.Graphics.Metal { var renderStage = stage == ShaderStage.Vertex ? MTLRenderStages.RenderStageVertex : MTLRenderStages.RenderStageFragment; - ulong[] resourceIds = new ulong[textures.Length + samplers.Length]; + Span resourceIds = stackalloc ulong[textures.Length + samplers.Length]; for (int i = 0; i < textures.Length; i++) { @@ -1193,14 +1194,14 @@ namespace Ryujinx.Graphics.Metal var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length); var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); return argBuffer.Range; } private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers) { - ulong[] resourceIds = new ulong[textures.Length + samplers.Length]; + Span resourceIds = stackalloc ulong[textures.Length + samplers.Length]; for (int i = 0; i < textures.Length; i++) { @@ -1230,7 +1231,7 @@ namespace Ryujinx.Graphics.Metal var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length); var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); return argBuffer.Range; } -- 2.47.1 From 97814b2852ae9302ca9764b39002992c7e946f43 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 22:26:27 +0100 Subject: [PATCH 255/368] Support non-index quad draws Fixes Deltarune --- .../IndexBufferPattern.cs | 141 ++++++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 90 +++++++++-- 2 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs b/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs new file mode 100644 index 000000000..7292b3134 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs @@ -0,0 +1,141 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal class IndexBufferPattern : IDisposable + { + public int PrimitiveVertices { get; } + public int PrimitiveVerticesOut { get; } + public int BaseIndex { get; } + public int[] OffsetIndex { get; } + public int IndexStride { get; } + public bool RepeatStart { get; } + + private readonly MetalRenderer _renderer; + private int _currentSize; + private BufferHandle _repeatingBuffer; + + public IndexBufferPattern(MetalRenderer renderer, + int primitiveVertices, + int primitiveVerticesOut, + int baseIndex, + int[] offsetIndex, + int indexStride, + bool repeatStart) + { + PrimitiveVertices = primitiveVertices; + PrimitiveVerticesOut = primitiveVerticesOut; + BaseIndex = baseIndex; + OffsetIndex = offsetIndex; + IndexStride = indexStride; + RepeatStart = repeatStart; + + _renderer = renderer; + } + + public int GetPrimitiveCount(int vertexCount) + { + return Math.Max(0, (vertexCount - BaseIndex) / IndexStride); + } + + public int GetConvertedCount(int indexCount) + { + int primitiveCount = GetPrimitiveCount(indexCount); + return primitiveCount * OffsetIndex.Length; + } + + public IEnumerable GetIndexMapping(int indexCount) + { + int primitiveCount = GetPrimitiveCount(indexCount); + int index = BaseIndex; + + for (int i = 0; i < primitiveCount; i++) + { + if (RepeatStart) + { + // Used for triangle fan + yield return 0; + } + + for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++) + { + yield return index + OffsetIndex[j]; + } + + index += IndexStride; + } + } + + public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount) + { + int primitiveCount = GetPrimitiveCount(vertexCount); + indexCount = primitiveCount * PrimitiveVerticesOut; + + int expectedSize = primitiveCount * OffsetIndex.Length; + + if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null) + { + return _repeatingBuffer; + } + + // Expand the repeating pattern to the number of requested primitives. + BufferHandle newBuffer = _renderer.BufferManager.CreateWithHandle(expectedSize * sizeof(int)); + + // Copy the old data to the new one. + if (_repeatingBuffer != BufferHandle.Null) + { + _renderer.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int)); + _renderer.BufferManager.Delete(_repeatingBuffer); + } + + _repeatingBuffer = newBuffer; + + // Add the additional repeats on top. + int newPrimitives = primitiveCount; + int oldPrimitives = (_currentSize) / OffsetIndex.Length; + + int[] newData; + + newPrimitives -= oldPrimitives; + newData = new int[expectedSize - _currentSize]; + + int outOffset = 0; + int index = oldPrimitives * IndexStride + BaseIndex; + + for (int i = 0; i < newPrimitives; i++) + { + if (RepeatStart) + { + // Used for triangle fan + newData[outOffset++] = 0; + } + + for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++) + { + newData[outOffset++] = index + OffsetIndex[j]; + } + + index += IndexStride; + } + + _renderer.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast(newData)); + _currentSize = expectedSize; + + return newBuffer; + } + + public void Dispose() + { + if (_repeatingBuffer != BufferHandle.Null) + { + _renderer.BufferManager.Delete(_repeatingBuffer); + _repeatingBuffer = BufferHandle.Null; + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 6167d3127..588037272 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -30,6 +30,9 @@ namespace Ryujinx.Graphics.Metal public readonly Action EndRenderPassDelegate; public MTLCommandBuffer CommandBuffer; + public IndexBufferPattern QuadsToTrisPattern; + public IndexBufferPattern TriFanToTrisPattern; + internal CommandBufferScoped? PreloadCbs { get; private set; } internal CommandBufferScoped Cbs { get; private set; } internal MTLCommandEncoder? CurrentEncoder { get; private set; } @@ -49,6 +52,9 @@ namespace Ryujinx.Graphics.Metal internal void InitEncoderStateManager(BufferManager bufferManager) { _encoderStateManager = new EncoderStateManager(_device, bufferManager, this); + + QuadsToTrisPattern = new IndexBufferPattern(_renderer, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false); + TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true); } public void SaveState() @@ -360,25 +366,89 @@ namespace Ryujinx.Graphics.Metal public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { + if (vertexCount == 0) + { + return; + } + var renderCommandEncoder = GetOrCreateRenderEncoder(true); - // TODO: Support topology re-indexing to provide support for TriangleFans - var primitiveType = _encoderStateManager.Topology.Convert(); + if (TopologyUnsupported(_encoderStateManager.Topology)) + { + var pattern = GetIndexBufferPattern(); - renderCommandEncoder.DrawPrimitives( - primitiveType, - (ulong)firstVertex, - (ulong)vertexCount, - (ulong)instanceCount, - (ulong)firstInstance); + BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount); + var buffer = _renderer.BufferManager.GetBuffer(handle, false); + var mtlBuffer = buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value; + + var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); + + renderCommandEncoder.DrawIndexedPrimitives( + primitiveType, + (ulong)indexCount, + MTLIndexType.UInt32, + mtlBuffer, + 0); + } + else + { + var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); + + renderCommandEncoder.DrawPrimitives( + primitiveType, + (ulong)firstVertex, + (ulong)vertexCount, + (ulong)instanceCount, + (ulong)firstInstance); + } + } + + private IndexBufferPattern GetIndexBufferPattern() + { + return _encoderStateManager.Topology switch + { + PrimitiveTopology.Quads => QuadsToTrisPattern, + PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => TriFanToTrisPattern, + _ => throw new NotSupportedException($"Unsupported topology: {_encoderStateManager.Topology}"), + }; + } + + private PrimitiveTopology TopologyRemap(PrimitiveTopology topology) + { + return topology switch + { + PrimitiveTopology.Quads => PrimitiveTopology.Triangles, + PrimitiveTopology.QuadStrip => PrimitiveTopology.TriangleStrip, + PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => PrimitiveTopology.Triangles, + _ => topology, + }; + } + + private bool TopologyUnsupported(PrimitiveTopology topology) + { + return topology switch + { + PrimitiveTopology.Quads or PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => true, + _ => false, + }; } public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) { + if (indexCount == 0) + { + return; + } + var renderCommandEncoder = GetOrCreateRenderEncoder(true); - // TODO: Support topology re-indexing to provide support for TriangleFans - var primitiveType = _encoderStateManager.Topology.Convert(); + // TODO: Reindex unsupported topologies + if (TopologyUnsupported(_encoderStateManager.Topology)) + { + Logger.Warning?.Print(LogClass.Gpu, $"Drawing indexed with unsupported topology: {_encoderStateManager.Topology}"); + } + + var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); var indexBuffer = _encoderStateManager.IndexBuffer; -- 2.47.1 From 16bc02ea2a292c605a7a3c40359f519ae335b084 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 22:34:21 +0100 Subject: [PATCH 256/368] Fix blend descriptors not dirting render pipeline Thanks peri --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 01d4247bc..8539895ca 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -497,6 +497,9 @@ namespace Ryujinx.Graphics.Metal { _currentState.BlendDescriptors[index] = blend; _currentState.BlendColor = blend.BlendConstant; + + // Mark dirty + _currentState.Dirty |= DirtyFlags.RenderPipeline; } // Inlineable -- 2.47.1 From 0b6bc12a65f461fa175547357fbc622411b0ba16 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 22:38:43 +0100 Subject: [PATCH 257/368] Fix CBP not doing its job Thanks peri (again) --- src/Ryujinx.Graphics.Metal/CommandBufferPool.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs index 925e4980b..ac8c45b20 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs @@ -27,6 +27,11 @@ namespace Ryujinx.Graphics.Metal public List Dependants; public List Waitables; + public void Reinitialize(MTLCommandQueue queue) + { + CommandBuffer = queue.CommandBuffer(); + } + public void Initialize(MTLCommandQueue queue) { CommandBuffer = queue.CommandBuffer(); @@ -218,7 +223,7 @@ namespace Ryujinx.Graphics.Metal commandBuffer.Commit(); // Replace entry with new MTLCommandBuffer - entry.Initialize(_queue); + entry.Reinitialize(_queue); int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers; _queuedIndexes[ptr] = cbIndex; -- 2.47.1 From de8e03c350b3ddca20c2b0d4b6be5f28b6887b7f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 23:52:38 +0100 Subject: [PATCH 258/368] Render target deduplication not sure if this is working --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 3 + .../EncoderStateManager.cs | 110 ++++++++++++++---- 2 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 3d5c61edd..6863282a8 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -98,6 +98,9 @@ namespace Ryujinx.Graphics.Metal // Changes to attachments take recreation! public Texture DepthStencil = default; public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments]; + public ITexture PreMaskDepthStencil = default; + public ITexture[] PreMaskRenderTargets; + public bool FramebufferUsingColorWriteMask; public MTLColorWriteMask[] RenderTargetMasks = Enumerable.Repeat(MTLColorWriteMask.All, Constants.MaxColorAttachments).ToArray(); public BlendDescriptor?[] BlendDescriptors = new BlendDescriptor?[Constants.MaxColorAttachments]; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 8539895ca..5f1aab365 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -429,6 +429,88 @@ namespace Ryujinx.Graphics.Metal public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) { + _currentState.FramebufferUsingColorWriteMask = false; + UpdateRenderTargetsInternal(colors, depthStencil); + } + + public void UpdateRenderTargetColorMasks(ReadOnlySpan componentMask) + { + _currentState.RenderTargetMasks = new MTLColorWriteMask[Constants.MaxColorAttachments]; + + for (int i = 0; i < componentMask.Length; i++) + { + bool red = (componentMask[i] & (0x1 << 0)) != 0; + bool green = (componentMask[i] & (0x1 << 1)) != 0; + bool blue = (componentMask[i] & (0x1 << 2)) != 0; + bool alpha = (componentMask[i] & (0x1 << 3)) != 0; + + var mask = MTLColorWriteMask.None; + + mask |= red ? MTLColorWriteMask.Red : 0; + mask |= green ? MTLColorWriteMask.Green : 0; + mask |= blue ? MTLColorWriteMask.Blue : 0; + mask |= alpha ? MTLColorWriteMask.Alpha : 0; + + _currentState.RenderTargetMasks[i] = mask; + } + + if (_currentState.FramebufferUsingColorWriteMask) + { + UpdateRenderTargetsInternal(_currentState.PreMaskRenderTargets, _currentState.PreMaskDepthStencil); + } + else + { + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + } + + private void UpdateRenderTargetsInternal(ITexture[] colors, ITexture depthStencil) + { + // TBDR GPUs don't work properly if the same attachment is bound to multiple targets, + // due to each attachment being a copy of the real attachment, rather than a direct write. + // + // Just try to remove duplicate attachments. + // Save a copy of the array to rebind when mask changes. + + // Look for textures that are masked out. + + for (int i = 0; i < colors.Length; i++) + { + if (colors[i] == null) + { + continue; + } + + ref var mtlMask = ref _currentState.RenderTargetMasks[i]; + + for (int j = 0; j < i; j++) + { + // Check each binding for a duplicate binding before it. + + if (colors[i] == colors[j]) + { + // Prefer the binding with no write mask. + + ref var mtlMask2 = ref _currentState.RenderTargetMasks[j]; + + if (mtlMask == 0) + { + colors[i] = null; + MaskOut(colors, depthStencil); + } + else if (mtlMask2 == 0) + { + colors[j] = null; + MaskOut(colors, depthStencil); + } + } + } + } + _currentState.RenderTargets = new Texture[Constants.MaxColorAttachments]; for (int i = 0; i < colors.Length; i++) @@ -457,32 +539,16 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdateRenderTargetColorMasks(ReadOnlySpan componentMask) + private void MaskOut(ITexture[] colors, ITexture depthStencil) { - _currentState.RenderTargetMasks = new MTLColorWriteMask[Constants.MaxColorAttachments]; - - for (int i = 0; i < componentMask.Length; i++) + if (!_currentState.FramebufferUsingColorWriteMask) { - bool red = (componentMask[i] & (0x1 << 0)) != 0; - bool green = (componentMask[i] & (0x1 << 1)) != 0; - bool blue = (componentMask[i] & (0x1 << 2)) != 0; - bool alpha = (componentMask[i] & (0x1 << 3)) != 0; - - var mask = MTLColorWriteMask.None; - - mask |= red ? MTLColorWriteMask.Red : 0; - mask |= green ? MTLColorWriteMask.Green : 0; - mask |= blue ? MTLColorWriteMask.Blue : 0; - mask |= alpha ? MTLColorWriteMask.Alpha : 0; - - _currentState.RenderTargetMasks[i] = mask; + _currentState.PreMaskRenderTargets = colors; + _currentState.PreMaskDepthStencil = depthStencil; } - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } + // If true, then the framebuffer must be recreated when the mask changes. + _currentState.FramebufferUsingColorWriteMask = true; } public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) -- 2.47.1 From 3b2beda27f8bcfa59541f4208f55dbfe72b8a809 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 27 Jun 2024 00:13:37 +0100 Subject: [PATCH 259/368] =?UTF-8?q?Don=E2=80=99t=20bind=20byte=20format=20?= =?UTF-8?q?converted=20index=20buffers=20at=20requested=20index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 5f1aab365..79189d0b9 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -378,7 +378,7 @@ namespace Ryujinx.Graphics.Metal if (type == GAL.IndexType.UByte) { _currentState.IndexType = MTLIndexType.UInt16; - _currentState.IndexBufferOffset = (ulong)buffer.Offset; + _currentState.IndexBufferOffset = 0; _currentState.IndexBuffer = _bufferManager.GetBufferI8ToI16(_pipeline.Cbs, buffer.Handle, buffer.Offset, buffer.Size); } else -- 2.47.1 From 36de337ac2859c81bd94ec8976a447a7b958de74 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 27 Jun 2024 00:20:00 +0100 Subject: [PATCH 260/368] Add constrained border colours to samplers (#26) --- src/Ryujinx.Graphics.Metal/Sampler.cs | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index 9f8ae74b4..7930627d4 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -14,9 +14,11 @@ namespace Ryujinx.Graphics.Metal { (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); + MTLSamplerBorderColor borderColor = GetConstrainedBorderColor(info.BorderColor, out _); + var samplerState = device.NewSamplerState(new MTLSamplerDescriptor { - BorderColor = MTLSamplerBorderColor.TransparentBlack, + BorderColor = borderColor, MinFilter = minFilter, MagFilter = info.MagFilter.Convert(), MipFilter = mipFilter, @@ -39,6 +41,37 @@ namespace Ryujinx.Graphics.Metal _mtlSamplerState = samplerState; } + private static MTLSamplerBorderColor GetConstrainedBorderColor(ColorF arbitraryBorderColor, out bool cantConstrain) + { + float r = arbitraryBorderColor.Red; + float g = arbitraryBorderColor.Green; + float b = arbitraryBorderColor.Blue; + float a = arbitraryBorderColor.Alpha; + + if (r == 0f && g == 0f && b == 0f) + { + if (a == 1f) + { + cantConstrain = false; + return MTLSamplerBorderColor.OpaqueBlack; + } + + if (a == 0f) + { + cantConstrain = false; + return MTLSamplerBorderColor.TransparentBlack; + } + } + else if (r == 1f && g == 1f && b == 1f && a == 1f) + { + cantConstrain = false; + return MTLSamplerBorderColor.OpaqueWhite; + } + + cantConstrain = true; + return MTLSamplerBorderColor.OpaqueBlack; + } + public MTLSamplerState GetSampler() { return _mtlSamplerState; -- 2.47.1 From 4f7b3fa0585f091a2b1b331bd3074d90a1084041 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 27 Jun 2024 22:51:12 +0100 Subject: [PATCH 261/368] CommandBufferBarrier --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 588037272..39361f710 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -285,14 +285,10 @@ namespace Ryujinx.Graphics.Metal { var computeCommandEncoder = GetOrCreateComputeEncoder(); - // TODO: Should there be a barrier on render targets? - var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures; + var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;; computeCommandEncoder.MemoryBarrier(scope); break; } - default: - Logger.Warning?.Print(LogClass.Gpu, "Barrier called outside of a render or compute pass"); - break; } } @@ -344,7 +340,7 @@ namespace Ryujinx.Graphics.Metal public void CommandBufferBarrier() { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + Barrier(); } public void CopyBuffer(BufferHandle src, BufferHandle dst, int srcOffset, int dstOffset, int size) @@ -701,9 +697,12 @@ namespace Ryujinx.Graphics.Metal public void TextureBarrier() { - var renderCommandEncoder = GetOrCreateRenderEncoder(); + if (CurrentEncoderType == EncoderType.Render) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment); + renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment); + } } public void TextureBarrierTiled() -- 2.47.1 From c160810bfc693373e7ee8eb253a8688c34afe2f9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jun 2024 21:13:59 +0100 Subject: [PATCH 262/368] Fragment input interpolation qualifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes Mario’s shadow in SMO --- .../CodeGen/Msl/Declarations.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index b15b482db..3179c80a2 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -299,6 +299,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { + string iq = string.Empty; + + if (context.Definitions.Stage == ShaderStage.Fragment) + { + iq = context.Definitions.ImapTypes[ioDefinition.Location].GetFirstUsedType() switch + { + PixelImap.Constant => "[[flat]] ", + PixelImap.ScreenLinear => "[[center_no_perspective]] ", + _ => string.Empty, + }; + } + string type = ioDefinition.IoVariable switch { // IoVariable.Position => "float4", @@ -329,7 +341,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl _ => "" }; - context.AppendLine($"{type} {name} {suffix};"); + context.AppendLine($"{type} {name} {iq}{suffix};"); } context.LeaveScope(";"); -- 2.47.1 From bbbc9e529d8e059a5fa8aff44cf5d7b0696bf7e5 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 28 Jun 2024 21:14:53 +0100 Subject: [PATCH 263/368] State and cache optimization (#27) * WIP pipeline/depth state cache rework * Fix some issues * Fix some more default values * Reduce allocations for state changes * fix helpershader stuff * explanation comment * fix depth bias --- .../ComputePipelineCache.cs | 36 -- .../DepthStencilCache.cs | 91 +++-- src/Ryujinx.Graphics.Metal/EncoderState.cs | 100 ++++-- .../EncoderStateManager.cs | 293 ++++++--------- src/Ryujinx.Graphics.Metal/HashTableSlim.cs | 143 ++++++++ src/Ryujinx.Graphics.Metal/HelperShader.cs | 56 ++- src/Ryujinx.Graphics.Metal/Pipeline.cs | 25 +- src/Ryujinx.Graphics.Metal/Program.cs | 60 ++++ .../RenderPipelineCache.cs | 248 ------------- .../State/DepthStencilUid.cs | 110 ++++++ .../State/PipelineState.cs | 338 ++++++++++++++++++ .../State/PipelineUid.cs | 200 +++++++++++ src/Ryujinx.Graphics.Metal/Texture.cs | 6 +- src/Ryujinx.Graphics.Metal/TextureBase.cs | 1 + 14 files changed, 1142 insertions(+), 565 deletions(-) delete mode 100644 src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs create mode 100644 src/Ryujinx.Graphics.Metal/HashTableSlim.cs delete mode 100644 src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs create mode 100644 src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs create mode 100644 src/Ryujinx.Graphics.Metal/State/PipelineState.cs create mode 100644 src/Ryujinx.Graphics.Metal/State/PipelineUid.cs diff --git a/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs b/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs deleted file mode 100644 index a76f4c33c..000000000 --- a/src/Ryujinx.Graphics.Metal/ComputePipelineCache.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Ryujinx.Common.Logging; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class ComputePipelineCache : StateCache - { - private readonly MTLDevice _device; - - public ComputePipelineCache(MTLDevice device) - { - _device = device; - } - - protected override MTLFunction GetHash(MTLFunction function) - { - return function; - } - - protected override MTLComputePipelineState CreateValue(MTLFunction function) - { - var error = new NSError(IntPtr.Zero); - var pipelineState = _device.NewComputePipelineState(function, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - return pipelineState; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs index be47653c0..bb6e4c180 100644 --- a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs +++ b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs @@ -1,28 +1,11 @@ +using Ryujinx.Graphics.Metal.State; using SharpMetal.Metal; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - struct DepthStencilHash - { - public struct StencilHash - { - public MTLStencilOperation StencilFailureOperation; - public MTLStencilOperation DepthFailureOperation; - public MTLStencilOperation DepthStencilPassOperation; - public MTLCompareFunction StencilCompareFunction; - public uint ReadMask; - public uint WriteMask; - } - public StencilHash FrontFace; - public StencilHash BackFace; - public MTLCompareFunction DepthCompareFunction; - public bool DepthWriteEnabled; - } - - [SupportedOSPlatform("macos")] - class DepthStencilCache : StateCache + class DepthStencilCache : StateCache { private readonly MTLDevice _device; @@ -31,41 +14,55 @@ namespace Ryujinx.Graphics.Metal _device = device; } - protected override DepthStencilHash GetHash(MTLDepthStencilDescriptor descriptor) + protected override DepthStencilUid GetHash(DepthStencilUid descriptor) { - var hash = new DepthStencilHash + return descriptor; + } + + protected override MTLDepthStencilState CreateValue(DepthStencilUid descriptor) + { + // Create descriptors + + ref StencilUid frontUid = ref descriptor.FrontFace; + + using var frontFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = frontUid.StencilFailureOperation, + DepthFailureOperation = frontUid.DepthFailureOperation, + DepthStencilPassOperation = frontUid.DepthStencilPassOperation, + StencilCompareFunction = frontUid.StencilCompareFunction, + ReadMask = frontUid.ReadMask, + WriteMask = frontUid.WriteMask + }; + + ref StencilUid backUid = ref descriptor.BackFace; + + using var backFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = backUid.StencilFailureOperation, + DepthFailureOperation = backUid.DepthFailureOperation, + DepthStencilPassOperation = backUid.DepthStencilPassOperation, + StencilCompareFunction = backUid.StencilCompareFunction, + ReadMask = backUid.ReadMask, + WriteMask = backUid.WriteMask + }; + + var mtlDescriptor = new MTLDepthStencilDescriptor { - // Front face - FrontFace = new DepthStencilHash.StencilHash - { - StencilFailureOperation = descriptor.FrontFaceStencil.StencilFailureOperation, - DepthFailureOperation = descriptor.FrontFaceStencil.DepthFailureOperation, - DepthStencilPassOperation = descriptor.FrontFaceStencil.DepthStencilPassOperation, - StencilCompareFunction = descriptor.FrontFaceStencil.StencilCompareFunction, - ReadMask = descriptor.FrontFaceStencil.ReadMask, - WriteMask = descriptor.FrontFaceStencil.WriteMask - }, - // Back face - BackFace = new DepthStencilHash.StencilHash - { - StencilFailureOperation = descriptor.BackFaceStencil.StencilFailureOperation, - DepthFailureOperation = descriptor.BackFaceStencil.DepthFailureOperation, - DepthStencilPassOperation = descriptor.BackFaceStencil.DepthStencilPassOperation, - StencilCompareFunction = descriptor.BackFaceStencil.StencilCompareFunction, - ReadMask = descriptor.BackFaceStencil.ReadMask, - WriteMask = descriptor.BackFaceStencil.WriteMask - }, - // Depth DepthCompareFunction = descriptor.DepthCompareFunction, DepthWriteEnabled = descriptor.DepthWriteEnabled }; - return hash; - } + if (descriptor.StencilTestEnabled) + { + mtlDescriptor.BackFaceStencil = backFaceStencil; + mtlDescriptor.FrontFaceStencil = frontFaceStencil; + } - protected override MTLDepthStencilState CreateValue(MTLDepthStencilDescriptor descriptor) - { - return _device.NewDepthStencilState(descriptor); + using (mtlDescriptor) + { + return _device.NewDepthStencilState(mtlDescriptor); + } } } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 6863282a8..2f732681b 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,4 +1,6 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Metal.State; using SharpMetal.Metal; using System; using System.Linq; @@ -48,12 +50,29 @@ namespace Ryujinx.Graphics.Metal } } - [SupportedOSPlatform("macos")] - struct EncoderState + struct PredrawState { - public MTLFunction? VertexFunction = null; - public MTLFunction? FragmentFunction = null; - public MTLFunction? ComputeFunction = null; + public MTLCullMode CullMode; + public DepthStencilUid DepthStencilUid; + public PrimitiveTopology Topology; + public MTLViewport[] Viewports; + } + + struct RenderTargetCopy + { + public MTLScissorRect[] Scissors; + public Texture DepthStencil; + public Texture[] RenderTargets; + } + + [SupportedOSPlatform("macos")] + class EncoderState + { + public Program RenderProgram = null; + public Program ComputeProgram = null; + + public PipelineState Pipeline; + public DepthStencilUid DepthStencilUid; public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage]; public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; @@ -71,21 +90,14 @@ namespace Ryujinx.Graphics.Metal public MTLIndexType IndexType = MTLIndexType.UInt16; public ulong IndexBufferOffset = 0; - public MTLDepthStencilState? DepthStencilState = null; - public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip; - public MTLCompareFunction DepthCompareFunction = MTLCompareFunction.Always; - public bool DepthWriteEnabled = false; public float DepthBias; public float SlopeScale; public float Clamp; - public MTLStencilDescriptor BackFaceStencil = new(); - public MTLStencilDescriptor FrontFaceStencil = new(); public int BackRefValue = 0; public int FrontRefValue = 0; - public bool StencilTestEnabled = false; public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; @@ -102,8 +114,7 @@ namespace Ryujinx.Graphics.Metal public ITexture[] PreMaskRenderTargets; public bool FramebufferUsingColorWriteMask; - public MTLColorWriteMask[] RenderTargetMasks = Enumerable.Repeat(MTLColorWriteMask.All, Constants.MaxColorAttachments).ToArray(); - public BlendDescriptor?[] BlendDescriptors = new BlendDescriptor?[Constants.MaxColorAttachments]; + public Array8 StoredBlend; public ColorF BlendColor = new(); public VertexBufferDescriptor[] VertexBuffers = []; @@ -115,25 +126,52 @@ namespace Ryujinx.Graphics.Metal // Only to be used for present public bool ClearLoadAction = false; - public EncoderState() { } - - public readonly EncoderState Clone() + public EncoderState() { - // Certain state (like viewport and scissor) doesn't need to be cloned, as it is always reacreated when assigned to - EncoderState clone = this; - clone.FragmentTextures = (TextureBase[])FragmentTextures.Clone(); - clone.FragmentSamplers = (MTLSamplerState[])FragmentSamplers.Clone(); - clone.VertexTextures = (TextureBase[])VertexTextures.Clone(); - clone.VertexSamplers = (MTLSamplerState[])VertexSamplers.Clone(); - clone.ComputeTextures = (TextureBase[])ComputeTextures.Clone(); - clone.ComputeSamplers = (MTLSamplerState[])ComputeSamplers.Clone(); - clone.BlendDescriptors = (BlendDescriptor?[])BlendDescriptors.Clone(); - clone.VertexBuffers = (VertexBufferDescriptor[])VertexBuffers.Clone(); - clone.VertexAttribs = (VertexAttribDescriptor[])VertexAttribs.Clone(); - clone.UniformBuffers = (BufferRef[])UniformBuffers.Clone(); - clone.StorageBuffers = (BufferRef[])StorageBuffers.Clone(); + Pipeline.Initialize(); + DepthStencilUid.DepthCompareFunction = MTLCompareFunction.Always; + } - return clone; + public RenderTargetCopy InheritForClear(EncoderState other, bool depth, int singleIndex = -1) + { + // Inherit render target related information without causing a render encoder split. + + var oldState = new RenderTargetCopy + { + Scissors = other.Scissors, + RenderTargets = other.RenderTargets, + DepthStencil = other.DepthStencil + }; + + Scissors = other.Scissors; + RenderTargets = other.RenderTargets; + DepthStencil = other.DepthStencil; + + Pipeline.ColorBlendAttachmentStateCount = other.Pipeline.ColorBlendAttachmentStateCount; + Pipeline.Internal.ColorBlendState = other.Pipeline.Internal.ColorBlendState; + Pipeline.DepthStencilFormat = other.Pipeline.DepthStencilFormat; + + ref var blendStates = ref Pipeline.Internal.ColorBlendState; + + // Mask out irrelevant attachments. + for (int i = 0; i < blendStates.Length; i++) + { + if (depth || (singleIndex != -1 && singleIndex != i)) + { + blendStates[i].WriteMask = MTLColorWriteMask.None; + } + } + + return oldState; + } + + public void Restore(RenderTargetCopy copy) + { + Scissors = copy.Scissors; + RenderTargets = copy.RenderTargets; + DepthStencil = copy.DepthStencil; + + Pipeline.Internal.ResetColorState(); } } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 79189d0b9..62c965697 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -1,9 +1,10 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Metal.State; using Ryujinx.Graphics.Shader; using SharpMetal.Metal; using System; -using System.Collections.Generic; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment; @@ -17,12 +18,10 @@ namespace Ryujinx.Graphics.Metal private readonly Pipeline _pipeline; private readonly BufferManager _bufferManager; - private readonly RenderPipelineCache _renderPipelineCache; - private readonly ComputePipelineCache _computePipelineCache; private readonly DepthStencilCache _depthStencilCache; - private EncoderState _currentState = new(); - private readonly Stack _backStates = []; + private readonly EncoderState _mainState = new(); + private EncoderState _currentState; public readonly Auto IndexBuffer => _currentState.IndexBuffer; public readonly MTLIndexType IndexType => _currentState.IndexType; @@ -41,9 +40,8 @@ namespace Ryujinx.Graphics.Metal _pipeline = pipeline; _bufferManager = bufferManager; - _renderPipelineCache = new(device); - _computePipelineCache = new(device); _depthStencilCache = new(device); + _currentState = _mainState; // Zero buffer byte[] zeros = new byte[ZeroBufferSize]; @@ -56,39 +54,38 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { // State - _currentState.FrontFaceStencil.Dispose(); - _currentState.BackFaceStencil.Dispose(); - _renderPipelineCache.Dispose(); - _computePipelineCache.Dispose(); _depthStencilCache.Dispose(); } - public void SaveState() + public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All) { - _backStates.Push(_currentState); - _currentState = _currentState.Clone(); + _currentState = state ?? _mainState; + + _currentState.Dirty |= flags; + + return _mainState; } - public void SaveAndResetState() + public PredrawState SavePredrawState() { - _backStates.Push(_currentState); - _currentState = new(); + return new PredrawState + { + CullMode = _currentState.CullMode, + DepthStencilUid = _currentState.DepthStencilUid, + Topology = _currentState.Topology, + Viewports = _currentState.Viewports.ToArray(), + }; } - public void RestoreState() + public void RestorePredrawState(PredrawState state) { - if (_backStates.Count > 0) - { - _currentState = _backStates.Pop(); + _currentState.CullMode = state.CullMode; + _currentState.DepthStencilUid = state.DepthStencilUid; + _currentState.Topology = state.Topology; + _currentState.Viewports = state.Viewports; - // Mark the other state as dirty - _currentState.Dirty |= DirtyFlags.All; - } - else - { - Logger.Error?.Print(LogClass.Gpu, "No state to restore"); - } + _currentState.Dirty |= DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports; } public void SetClearLoadAction(bool clear) @@ -267,106 +264,25 @@ namespace Ryujinx.Graphics.Metal private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + MTLRenderPipelineState pipelineState = _currentState.Pipeline.CreateRenderPipeline(_device, _currentState.RenderProgram); - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - if (_currentState.RenderTargets[i] != null) - { - var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].GetHandle().PixelFormat; - pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - pipelineAttachment.WriteMask = _currentState.RenderTargetMasks[i]; + renderCommandEncoder.SetRenderPipelineState(pipelineState); - if (_currentState.BlendDescriptors[i] != null) - { - var blendDescriptor = _currentState.BlendDescriptors[i].Value; - pipelineAttachment.SetBlendingEnabled(blendDescriptor.Enable); - pipelineAttachment.AlphaBlendOperation = blendDescriptor.AlphaOp.Convert(); - pipelineAttachment.RgbBlendOperation = blendDescriptor.ColorOp.Convert(); - pipelineAttachment.SourceAlphaBlendFactor = blendDescriptor.AlphaSrcFactor.Convert(); - pipelineAttachment.DestinationAlphaBlendFactor = blendDescriptor.AlphaDstFactor.Convert(); - pipelineAttachment.SourceRGBBlendFactor = blendDescriptor.ColorSrcFactor.Convert(); - pipelineAttachment.DestinationRGBBlendFactor = blendDescriptor.ColorDstFactor.Convert(); - } - } - } - - if (_currentState.DepthStencil != null) - { - switch (_currentState.DepthStencil.GetHandle().PixelFormat) - { - // Depth Only Attachment - case MTLPixelFormat.Depth16Unorm: - case MTLPixelFormat.Depth32Float: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat; - break; - - // Stencil Only Attachment - case MTLPixelFormat.Stencil8: - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat; - break; - - // Combined Attachment - case MTLPixelFormat.Depth24UnormStencil8: - case MTLPixelFormat.Depth32FloatStencil8: - renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat; - renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat; - break; - default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.GetHandle().PixelFormat}!"); - break; - } - } - - var vertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs); - renderPipelineDescriptor.VertexDescriptor = vertexDescriptor; - - try - { - if (_currentState.VertexFunction != null) - { - renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; - } - else - { - return; - } - - if (_currentState.FragmentFunction != null) - { - renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; - } - - var pipelineState = _renderPipelineCache.GetOrCreate(renderPipelineDescriptor); - - renderCommandEncoder.SetRenderPipelineState(pipelineState); - - renderCommandEncoder.SetBlendColor( - _currentState.BlendColor.Red, - _currentState.BlendColor.Green, - _currentState.BlendColor.Blue, - _currentState.BlendColor.Alpha); - } - finally - { - // Cleanup - renderPipelineDescriptor.Dispose(); - vertexDescriptor.Dispose(); - } + renderCommandEncoder.SetBlendColor( + _currentState.BlendColor.Red, + _currentState.BlendColor.Green, + _currentState.BlendColor.Blue, + _currentState.BlendColor.Alpha); } private void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) { - if (_currentState.ComputeFunction == null) + if (_currentState.ComputeProgram == null) { return; } - var pipelineState = _computePipelineCache.GetOrCreate(_currentState.ComputeFunction.Value); + var pipelineState = PipelineState.CreateComputePipeline(_device, _currentState.ComputeProgram); computeCommandEncoder.SetComputePipelineState(pipelineState); } @@ -414,14 +330,13 @@ namespace Ryujinx.Graphics.Metal if (prg.VertexFunction != IntPtr.Zero) { - _currentState.VertexFunction = prg.VertexFunction; - _currentState.FragmentFunction = prg.FragmentFunction; + _currentState.RenderProgram = prg; _currentState.Dirty |= DirtyFlags.RenderPipeline; } - if (prg.ComputeFunction != IntPtr.Zero) + else if (prg.ComputeFunction != IntPtr.Zero) { - _currentState.ComputeFunction = prg.ComputeFunction; + _currentState.ComputeProgram = prg; _currentState.Dirty |= DirtyFlags.ComputePipeline; } @@ -435,7 +350,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateRenderTargetColorMasks(ReadOnlySpan componentMask) { - _currentState.RenderTargetMasks = new MTLColorWriteMask[Constants.MaxColorAttachments]; + ref var blendState = ref _currentState.Pipeline.Internal.ColorBlendState; for (int i = 0; i < componentMask.Length; i++) { @@ -451,7 +366,25 @@ namespace Ryujinx.Graphics.Metal mask |= blue ? MTLColorWriteMask.Blue : 0; mask |= alpha ? MTLColorWriteMask.Alpha : 0; - _currentState.RenderTargetMasks[i] = mask; + ref ColorBlendStateUid mtlBlend = ref blendState[i]; + + // When color write mask is 0, remove all blend state to help the pipeline cache. + // Restore it when the mask becomes non-zero. + if (mtlBlend.WriteMask != mask) + { + if (mask == 0) + { + _currentState.StoredBlend[i] = mtlBlend; + + mtlBlend = new ColorBlendStateUid(); + } + else if (mtlBlend.WriteMask == 0) + { + mtlBlend = _currentState.StoredBlend[i]; + } + } + + blendState[i].WriteMask = mask; } if (_currentState.FramebufferUsingColorWriteMask) @@ -478,6 +411,11 @@ namespace Ryujinx.Graphics.Metal // Look for textures that are masked out. + ref PipelineState pipeline = ref _currentState.Pipeline; + ref var blendState = ref pipeline.Internal.ColorBlendState; + + pipeline.ColorBlendAttachmentStateCount = (uint)colors.Length; + for (int i = 0; i < colors.Length; i++) { if (colors[i] == null) @@ -485,7 +423,7 @@ namespace Ryujinx.Graphics.Metal continue; } - ref var mtlMask = ref _currentState.RenderTargetMasks[i]; + var mtlMask = blendState[i].WriteMask; for (int j = 0; j < i; j++) { @@ -495,7 +433,7 @@ namespace Ryujinx.Graphics.Metal { // Prefer the binding with no write mask. - ref var mtlMask2 = ref _currentState.RenderTargetMasks[j]; + var mtlMask2 = blendState[j].WriteMask; if (mtlMask == 0) { @@ -517,18 +455,23 @@ namespace Ryujinx.Graphics.Metal { if (colors[i] is not Texture tex) { + blendState[i].PixelFormat = MTLPixelFormat.Invalid; + continue; } + blendState[i].PixelFormat = tex.GetHandle().PixelFormat; // TODO: cache this _currentState.RenderTargets[i] = tex; } if (depthStencil is Texture depthTexture) { + pipeline.DepthStencilFormat = depthTexture.GetHandle().PixelFormat; // TODO: cache this _currentState.DepthStencil = depthTexture; } else if (depthStencil == null) { + pipeline.DepthStencilFormat = MTLPixelFormat.Invalid; _currentState.DepthStencil = null; } @@ -555,13 +498,32 @@ namespace Ryujinx.Graphics.Metal { _currentState.VertexAttribs = vertexAttribs.ToArray(); + // Update the buffers on the pipeline + UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); + // Mark dirty _currentState.Dirty |= DirtyFlags.RenderPipeline; } public void UpdateBlendDescriptors(int index, BlendDescriptor blend) { - _currentState.BlendDescriptors[index] = blend; + ref var blendState = ref _currentState.Pipeline.Internal.ColorBlendState[index]; + + blendState.Enable = blend.Enable; + blendState.AlphaBlendOperation = blend.AlphaOp.Convert(); + blendState.RgbBlendOperation = blend.ColorOp.Convert(); + blendState.SourceAlphaBlendFactor = blend.AlphaSrcFactor.Convert(); + blendState.DestinationAlphaBlendFactor = blend.AlphaDstFactor.Convert(); + blendState.SourceRGBBlendFactor = blend.ColorSrcFactor.Convert(); + blendState.DestinationRGBBlendFactor = blend.ColorDstFactor.Convert(); + + if (blendState.WriteMask == 0) + { + _currentState.StoredBlend[index] = blendState; + + blendState = new ColorBlendStateUid(); + } + _currentState.BlendColor = blend.BlendConstant; // Mark dirty @@ -571,7 +533,9 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateStencilState(StencilTestDescriptor stencilTest) { - _currentState.FrontFaceStencil = new MTLStencilDescriptor + ref DepthStencilUid uid = ref _currentState.DepthStencilUid; + + uid.FrontFace = new StencilUid { StencilFailureOperation = stencilTest.FrontSFail.Convert(), DepthFailureOperation = stencilTest.FrontDpFail.Convert(), @@ -581,7 +545,7 @@ namespace Ryujinx.Graphics.Metal WriteMask = (uint)stencilTest.FrontMask }; - _currentState.BackFaceStencil = new MTLStencilDescriptor + uid.BackFace = new StencilUid { StencilFailureOperation = stencilTest.BackSFail.Convert(), DepthFailureOperation = stencilTest.BackDpFail.Convert(), @@ -591,55 +555,23 @@ namespace Ryujinx.Graphics.Metal WriteMask = (uint)stencilTest.BackMask }; - _currentState.StencilTestEnabled = stencilTest.TestEnable; - - var descriptor = new MTLDepthStencilDescriptor - { - DepthCompareFunction = _currentState.DepthCompareFunction, - DepthWriteEnabled = _currentState.DepthWriteEnabled - }; - - if (_currentState.StencilTestEnabled) - { - descriptor.BackFaceStencil = _currentState.BackFaceStencil; - descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; - } - - _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); + uid.StencilTestEnabled = stencilTest.TestEnable; UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); // Mark dirty _currentState.Dirty |= DirtyFlags.DepthStencil; - - // Cleanup - descriptor.Dispose(); } public void UpdateDepthState(DepthTestDescriptor depthTest) { - _currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; - _currentState.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable; + ref DepthStencilUid uid = ref _currentState.DepthStencilUid; - var descriptor = new MTLDepthStencilDescriptor - { - DepthCompareFunction = _currentState.DepthCompareFunction, - DepthWriteEnabled = _currentState.DepthWriteEnabled - }; - - if (_currentState.StencilTestEnabled) - { - descriptor.BackFaceStencil = _currentState.BackFaceStencil; - descriptor.FrontFaceStencil = _currentState.FrontFaceStencil; - } - - _currentState.DepthStencilState = _device.NewDepthStencilState(descriptor); + uid.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; + uid.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable; // Mark dirty _currentState.Dirty |= DirtyFlags.DepthStencil; - - // Cleanup - descriptor.Dispose(); } // Inlineable @@ -751,6 +683,9 @@ namespace Ryujinx.Graphics.Metal { _currentState.VertexBuffers = vertexBuffers.ToArray(); + // Update the buffers on the pipeline + UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); + // Inline update if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) { @@ -925,10 +860,9 @@ namespace Ryujinx.Graphics.Metal private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.DepthStencilState != null) - { - renderCommandEncoder.SetDepthStencilState(_currentState.DepthStencilState.Value); - } + MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); + + renderCommandEncoder.SetDepthStencilState(state); } private readonly void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder) @@ -973,16 +907,17 @@ namespace Ryujinx.Graphics.Metal } } - private readonly MTLVertexDescriptor BuildVertexDescriptor(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) + private void UpdatePipelineVertexState(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) { - var vertexDescriptor = new MTLVertexDescriptor(); + ref PipelineState pipeline = ref _currentState.Pipeline; uint indexMask = 0; for (int i = 0; i < attribDescriptors.Length; i++) { + ref var attrib = ref pipeline.Internal.VertexAttributes[i]; + if (attribDescriptors[i].IsZero) { - var attrib = vertexDescriptor.Attributes.Object((ulong)i); attrib.Format = attribDescriptors[i].Format.Convert(); indexMask |= 1u << (int)Constants.ZeroBufferIndex; attrib.BufferIndex = Constants.ZeroBufferIndex; @@ -990,7 +925,6 @@ namespace Ryujinx.Graphics.Metal } else { - var attrib = vertexDescriptor.Attributes.Object((ulong)i); attrib.Format = attribDescriptors[i].Format.Convert(); indexMask |= 1u << attribDescriptors[i].BufferIndex; attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; @@ -1000,11 +934,11 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < bufferDescriptors.Length; i++) { - var layout = vertexDescriptor.Layouts.Object((ulong)i); + ref var layout = ref pipeline.Internal.VertexBindings[i]; if ((indexMask & (1u << i)) != 0) { - layout.Stride = (ulong)bufferDescriptors[i].Stride; + layout.Stride = (uint)bufferDescriptors[i].Stride; if (layout.Stride == 0) { @@ -1017,7 +951,7 @@ namespace Ryujinx.Graphics.Metal if (bufferDescriptors[i].Divisor > 0) { layout.StepFunction = MTLVertexStepFunction.PerInstance; - layout.StepRate = (ulong)bufferDescriptors[i].Divisor; + layout.StepRate = (uint)bufferDescriptors[i].Divisor; } else { @@ -1028,20 +962,21 @@ namespace Ryujinx.Graphics.Metal } else { - layout.Stride = 0; + layout = new(); } } // Zero buffer if ((indexMask & (1u << (int)Constants.ZeroBufferIndex)) != 0) { - var layout = vertexDescriptor.Layouts.Object(Constants.ZeroBufferIndex); + ref var layout = ref pipeline.Internal.VertexBindings[(int)Constants.ZeroBufferIndex]; layout.Stride = 1; layout.StepFunction = MTLVertexStepFunction.Constant; layout.StepRate = 0; } - return vertexDescriptor; + pipeline.VertexAttributeDescriptionsCount = (uint)attribDescriptors.Length; + pipeline.VertexBindingDescriptionsCount = Constants.ZeroBufferIndex + 1; // TODO: move this out? } private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) diff --git a/src/Ryujinx.Graphics.Metal/HashTableSlim.cs b/src/Ryujinx.Graphics.Metal/HashTableSlim.cs new file mode 100644 index 000000000..a27a53d47 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/HashTableSlim.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Graphics.Metal +{ + interface IRefEquatable + { + bool Equals(ref T other); + } + + class HashTableSlim where TKey : IRefEquatable + { + private const int TotalBuckets = 16; // Must be power of 2 + private const int TotalBucketsMask = TotalBuckets - 1; + + private struct Entry + { + public int Hash; + public TKey Key; + public TValue Value; + } + + private struct Bucket + { + public int Length; + public Entry[] Entries; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return Entries == null ? Span.Empty : Entries.AsSpan(0, Length); + } + } + + private readonly Bucket[] _hashTable = new Bucket[TotalBuckets]; + + public IEnumerable Keys + { + get + { + foreach (Bucket bucket in _hashTable) + { + for (int i = 0; i < bucket.Length; i++) + { + yield return bucket.Entries[i].Key; + } + } + } + } + + public IEnumerable Values + { + get + { + foreach (Bucket bucket in _hashTable) + { + for (int i = 0; i < bucket.Length; i++) + { + yield return bucket.Entries[i].Value; + } + } + } + } + + public void Add(ref TKey key, TValue value) + { + var entry = new Entry + { + Hash = key.GetHashCode(), + Key = key, + Value = value, + }; + + int hashCode = key.GetHashCode(); + int bucketIndex = hashCode & TotalBucketsMask; + + ref var bucket = ref _hashTable[bucketIndex]; + if (bucket.Entries != null) + { + int index = bucket.Length; + + if (index >= bucket.Entries.Length) + { + Array.Resize(ref bucket.Entries, index + 1); + } + + bucket.Entries[index] = entry; + } + else + { + bucket.Entries = new[] + { + entry, + }; + } + + bucket.Length++; + } + + public bool Remove(ref TKey key) + { + int hashCode = key.GetHashCode(); + + ref var bucket = ref _hashTable[hashCode & TotalBucketsMask]; + var entries = bucket.AsSpan(); + for (int i = 0; i < entries.Length; i++) + { + ref var entry = ref entries[i]; + + if (entry.Hash == hashCode && entry.Key.Equals(ref key)) + { + entries[(i + 1)..].CopyTo(entries[i..]); + bucket.Length--; + + return true; + } + } + + return false; + } + + public bool TryGetValue(ref TKey key, out TValue value) + { + int hashCode = key.GetHashCode(); + + var entries = _hashTable[hashCode & TotalBucketsMask].AsSpan(); + for (int i = 0; i < entries.Length; i++) + { + ref var entry = ref entries[i]; + + if (entry.Hash == hashCode && entry.Key.Equals(ref key)) + { + value = entry.Value; + return true; + } + } + + value = default; + return false; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 1387a5a35..ec944b0f8 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -25,6 +25,8 @@ namespace Ryujinx.Graphics.Metal private readonly IProgram _programDepthStencilClear; private readonly IProgram _programStrideChange; + private readonly EncoderState _helperShaderState = new(); + public HelperShader(MTLDevice device, MetalRenderer renderer, Pipeline pipeline) { _device = device; @@ -80,8 +82,7 @@ namespace Ryujinx.Graphics.Metal bool linearFilter, bool clear = false) { - // Save current state - _pipeline.SaveAndResetState(); + _pipeline.SwapState(_helperShaderState); const int RegionBufferSize = 16; @@ -141,8 +142,14 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); + // Cleanup + if (clear) + { + _pipeline.SetClearLoadAction(false); + } + // Restore previous state - _pipeline.RestoreState(); + _pipeline.SwapState(null); } public unsafe void DrawTexture( @@ -152,7 +159,11 @@ namespace Ryujinx.Graphics.Metal Extents2DF dstRegion) { // Save current state - _pipeline.SaveState(); + var state = _pipeline.SavePredrawState(); + + _pipeline.SetFaceCulling(false, Face.Front); + _pipeline.SetStencilTest(new StencilTestDescriptor()); + _pipeline.SetDepthTest(new DepthTestDescriptor()); const int RegionBufferSize = 16; @@ -204,7 +215,7 @@ namespace Ryujinx.Graphics.Metal _renderer.BufferManager.Delete(bufferHandle); // Restore previous state - _pipeline.RestoreState(); + _pipeline.RestorePredrawState(state); } public void ConvertI8ToI16(CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size) @@ -229,7 +240,7 @@ namespace Ryujinx.Graphics.Metal const int ParamsBufferSize = 16; // Save current state - _pipeline.SaveAndResetState(); + _pipeline.SwapState(_helperShaderState); Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; @@ -252,7 +263,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 64, 1, 1); // Restore previous state - _pipeline.RestoreState(); + _pipeline.SwapState(null); } public unsafe void ClearColor( @@ -262,8 +273,14 @@ namespace Ryujinx.Graphics.Metal int dstWidth, int dstHeight) { + // Keep original scissor + DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors); + // Save current state - _pipeline.SaveState(); + EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags); + + // Inherit some state without fully recreating render pipeline. + RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, false, index); const int ClearColorBufferSize = 16; @@ -286,7 +303,7 @@ namespace Ryujinx.Graphics.Metal 1f); _pipeline.SetProgram(_programsColorClear[index]); - _pipeline.SetBlendState(index, new BlendDescriptor(false, new ColorF(0f, 0f, 0f, 1f), BlendOp.Add, BlendFactor.One, BlendFactor.Zero, BlendOp.Add, BlendFactor.One, BlendFactor.Zero)); + _pipeline.SetBlendState(index, new BlendDescriptor()); _pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); _pipeline.SetRenderTargetColorMasks([componentMask]); @@ -295,7 +312,9 @@ namespace Ryujinx.Graphics.Metal _pipeline.Draw(4, 1, 0, 0); // Restore previous state - _pipeline.RestoreState(); + _pipeline.SwapState(null, clearFlags); + + _helperShaderState.Restore(save); } public unsafe void ClearDepthStencil( @@ -306,8 +325,15 @@ namespace Ryujinx.Graphics.Metal int dstWidth, int dstHeight) { + // Keep original scissor + DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors); + var helperScissors = _helperShaderState.Scissors; + // Save current state - _pipeline.SaveState(); + EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags); + + // Inherit some state without fully recreating render pipeline. + RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, true); const int ClearDepthBufferSize = 16; @@ -334,8 +360,14 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); _pipeline.Draw(4, 1, 0, 0); + // Cleanup + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); + _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); + // Restore previous state - _pipeline.RestoreState(); + _pipeline.SwapState(null, clearFlags); + + _helperShaderState.Restore(save); } private static StencilTestDescriptor CreateStencilTestDescriptor( diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 39361f710..6363eb5d8 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -57,19 +57,19 @@ namespace Ryujinx.Graphics.Metal TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true); } - public void SaveState() + public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All) { - _encoderStateManager.SaveState(); + return _encoderStateManager.SwapState(state, flags); } - public void SaveAndResetState() + public PredrawState SavePredrawState() { - _encoderStateManager.SaveAndResetState(); + return _encoderStateManager.SavePredrawState(); } - public void RestoreState() + public void RestorePredrawState(PredrawState state) { - _encoderStateManager.RestoreState(); + _encoderStateManager.RestorePredrawState(state); } public void SetClearLoadAction(bool clear) @@ -240,8 +240,6 @@ namespace Ryujinx.Graphics.Metal public void FlushCommandsImpl() { - SaveState(); - EndCurrentPass(); _byteWeight = 0; @@ -254,8 +252,6 @@ namespace Ryujinx.Graphics.Metal CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; _renderer.RegisterFlush(); - - RestoreState(); } public void BlitColor( @@ -511,7 +507,14 @@ namespace Ryujinx.Graphics.Metal public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) { - _encoderStateManager.UpdateDepthBias(units, factor, clamp); + if (enables == 0) + { + _encoderStateManager.UpdateDepthBias(0, 0, 0); + } + else + { + _encoderStateManager.UpdateDepthBias(units, factor, clamp); + } } public void SetDepthClamp(bool clamp) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 89f0bd5dd..40cb6df77 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -16,6 +16,10 @@ namespace Ryujinx.Graphics.Metal public MTLFunction FragmentFunction; public MTLFunction ComputeFunction; + private HashTableSlim _graphicsPipelineCache; + private MTLComputePipelineState? _computePipelineCache; + private bool _firstBackgroundUse; + public Program(ShaderSource[] shaders, MTLDevice device) { for (int index = 0; index < shaders.Length; index++) @@ -62,8 +66,64 @@ namespace Ryujinx.Graphics.Metal return ""u8.ToArray(); } + public void AddGraphicsPipeline(ref PipelineUid key, MTLRenderPipelineState pipeline) + { + (_graphicsPipelineCache ??= new()).Add(ref key, pipeline); + } + + public void AddComputePipeline(MTLComputePipelineState pipeline) + { + _computePipelineCache = pipeline; + } + + public bool TryGetGraphicsPipeline(ref PipelineUid key, out MTLRenderPipelineState pipeline) + { + if (_graphicsPipelineCache == null) + { + pipeline = default; + return false; + } + + if (!_graphicsPipelineCache.TryGetValue(ref key, out pipeline)) + { + if (_firstBackgroundUse) + { + Logger.Warning?.Print(LogClass.Gpu, "Background pipeline compile missed on draw - incorrect pipeline state?"); + _firstBackgroundUse = false; + } + + return false; + } + + _firstBackgroundUse = false; + + return true; + } + + public bool TryGetComputePipeline(out MTLComputePipelineState pipeline) + { + if (_computePipelineCache.HasValue) + { + pipeline = _computePipelineCache.Value; + return true; + } + + pipeline = default; + return false; + } + public void Dispose() { + if (_graphicsPipelineCache != null) + { + foreach (MTLRenderPipelineState pipeline in _graphicsPipelineCache.Values) + { + pipeline.Dispose(); + } + } + + _computePipelineCache?.Dispose(); + VertexFunction.Dispose(); FragmentFunction.Dispose(); ComputeFunction.Dispose(); diff --git a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs b/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs deleted file mode 100644 index b8e6005c4..000000000 --- a/src/Ryujinx.Graphics.Metal/RenderPipelineCache.cs +++ /dev/null @@ -1,248 +0,0 @@ -using Ryujinx.Common.Logging; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - struct RenderPipelineHash - { - public MTLFunction VertexFunction; - public MTLFunction FragmentFunction; - public struct ColorAttachmentHash - { - public MTLPixelFormat PixelFormat; - public bool BlendingEnabled; - public MTLBlendOperation RgbBlendOperation; - public MTLBlendOperation AlphaBlendOperation; - public MTLBlendFactor SourceRGBBlendFactor; - public MTLBlendFactor DestinationRGBBlendFactor; - public MTLBlendFactor SourceAlphaBlendFactor; - public MTLBlendFactor DestinationAlphaBlendFactor; - public MTLColorWriteMask WriteMask; - } - [System.Runtime.CompilerServices.InlineArray(Constants.MaxColorAttachments)] - public struct ColorAttachmentHashArray - { - public ColorAttachmentHash data; - } - public ColorAttachmentHashArray ColorAttachments; - public struct DepthStencilAttachmentHash - { - public MTLPixelFormat DepthPixelFormat; - public MTLPixelFormat StencilPixelFormat; - } - public DepthStencilAttachmentHash DepthStencilAttachment; - public struct VertexDescriptorHash - { - public struct AttributeHash - { - public MTLVertexFormat Format; - public ulong Offset; - public ulong BufferIndex; - } - [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexAttributes)] - public struct AttributeHashArray - { - public AttributeHash data; - } - public AttributeHashArray Attributes; - public struct LayoutHash - { - public ulong Stride; - public MTLVertexStepFunction StepFunction; - public ulong StepRate; - } - [System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexLayouts)] - public struct LayoutHashArray - { - public LayoutHash data; - } - public LayoutHashArray Layouts; - } - public VertexDescriptorHash VertexDescriptor; - - public override bool Equals(object obj) - { - if (obj is not RenderPipelineHash other) - { - return false; - } - - if (VertexFunction != other.VertexFunction) - { - return false; - } - if (FragmentFunction != other.FragmentFunction) - { - return false; - } - if (DepthStencilAttachment.DepthPixelFormat != other.DepthStencilAttachment.DepthPixelFormat) - { - return false; - } - if (DepthStencilAttachment.StencilPixelFormat != other.DepthStencilAttachment.StencilPixelFormat) - { - return false; - } - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - if (ColorAttachments[i].PixelFormat != other.ColorAttachments[i].PixelFormat) - { - return false; - } - if (ColorAttachments[i].BlendingEnabled != other.ColorAttachments[i].BlendingEnabled) - { - return false; - } - if (ColorAttachments[i].RgbBlendOperation != other.ColorAttachments[i].RgbBlendOperation) - { - return false; - } - if (ColorAttachments[i].AlphaBlendOperation != other.ColorAttachments[i].AlphaBlendOperation) - { - return false; - } - if (ColorAttachments[i].SourceRGBBlendFactor != other.ColorAttachments[i].SourceRGBBlendFactor) - { - return false; - } - if (ColorAttachments[i].DestinationRGBBlendFactor != other.ColorAttachments[i].DestinationRGBBlendFactor) - { - return false; - } - if (ColorAttachments[i].SourceAlphaBlendFactor != other.ColorAttachments[i].SourceAlphaBlendFactor) - { - return false; - } - if (ColorAttachments[i].DestinationAlphaBlendFactor != other.ColorAttachments[i].DestinationAlphaBlendFactor) - { - return false; - } - if (ColorAttachments[i].WriteMask != other.ColorAttachments[i].WriteMask) - { - return false; - } - } - for (int i = 0; i < Constants.MaxVertexAttributes; i++) - { - if (VertexDescriptor.Attributes[i].Format != other.VertexDescriptor.Attributes[i].Format) - { - return false; - } - if (VertexDescriptor.Attributes[i].Offset != other.VertexDescriptor.Attributes[i].Offset) - { - return false; - } - if (VertexDescriptor.Attributes[i].BufferIndex != other.VertexDescriptor.Attributes[i].BufferIndex) - { - return false; - } - } - for (int i = 0; i < Constants.MaxVertexLayouts; i++) - { - if (VertexDescriptor.Layouts[i].Stride != other.VertexDescriptor.Layouts[i].Stride) - { - return false; - } - if (VertexDescriptor.Layouts[i].StepFunction != other.VertexDescriptor.Layouts[i].StepFunction) - { - return false; - } - if (VertexDescriptor.Layouts[i].StepRate != other.VertexDescriptor.Layouts[i].StepRate) - { - return false; - } - } - - return true; - } - } - - [SupportedOSPlatform("macos")] - class RenderPipelineCache : StateCache - { - private readonly MTLDevice _device; - - public RenderPipelineCache(MTLDevice device) - { - _device = device; - } - - protected override RenderPipelineHash GetHash(MTLRenderPipelineDescriptor descriptor) - { - var hash = new RenderPipelineHash - { - // Functions - VertexFunction = descriptor.VertexFunction, - FragmentFunction = descriptor.FragmentFunction, - DepthStencilAttachment = new RenderPipelineHash.DepthStencilAttachmentHash - { - DepthPixelFormat = descriptor.DepthAttachmentPixelFormat, - StencilPixelFormat = descriptor.StencilAttachmentPixelFormat - }, - }; - - // Color Attachments - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - var attachment = descriptor.ColorAttachments.Object((ulong)i); - hash.ColorAttachments[i] = new RenderPipelineHash.ColorAttachmentHash - { - PixelFormat = attachment.PixelFormat, - BlendingEnabled = attachment.BlendingEnabled, - RgbBlendOperation = attachment.RgbBlendOperation, - AlphaBlendOperation = attachment.AlphaBlendOperation, - SourceRGBBlendFactor = attachment.SourceRGBBlendFactor, - DestinationRGBBlendFactor = attachment.DestinationRGBBlendFactor, - SourceAlphaBlendFactor = attachment.SourceAlphaBlendFactor, - DestinationAlphaBlendFactor = attachment.DestinationAlphaBlendFactor, - WriteMask = attachment.WriteMask - }; - } - - // Vertex descriptor - hash.VertexDescriptor = new RenderPipelineHash.VertexDescriptorHash(); - - // Attributes - for (int i = 0; i < Constants.MaxVertexAttributes; i++) - { - var attribute = descriptor.VertexDescriptor.Attributes.Object((ulong)i); - hash.VertexDescriptor.Attributes[i] = new RenderPipelineHash.VertexDescriptorHash.AttributeHash - { - Format = attribute.Format, - Offset = attribute.Offset, - BufferIndex = attribute.BufferIndex - }; - } - - // Layouts - for (int i = 0; i < Constants.MaxVertexLayouts; i++) - { - var layout = descriptor.VertexDescriptor.Layouts.Object((ulong)i); - hash.VertexDescriptor.Layouts[i] = new RenderPipelineHash.VertexDescriptorHash.LayoutHash - { - Stride = layout.Stride, - StepFunction = layout.StepFunction, - StepRate = layout.StepRate - }; - } - - return hash; - } - - protected override MTLRenderPipelineState CreateValue(MTLRenderPipelineDescriptor descriptor) - { - var error = new NSError(IntPtr.Zero); - var pipelineState = _device.NewRenderPipelineState(descriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - return pipelineState; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs b/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs new file mode 100644 index 000000000..63b1d8ef4 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs @@ -0,0 +1,110 @@ +using SharpMetal.Metal; +using System; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace Ryujinx.Graphics.Metal.State +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct StencilUid + { + public uint ReadMask; + public uint WriteMask; + public ushort Operations; + + public MTLStencilOperation StencilFailureOperation + { + readonly get => (MTLStencilOperation)((Operations >> 0) & 0xF); + set => Operations = (ushort)((Operations & 0xFFF0) | ((int)value << 0)); + } + + public MTLStencilOperation DepthFailureOperation + { + readonly get => (MTLStencilOperation)((Operations >> 4) & 0xF); + set => Operations = (ushort)((Operations & 0xFF0F) | ((int)value << 4)); + } + + public MTLStencilOperation DepthStencilPassOperation + { + readonly get => (MTLStencilOperation)((Operations >> 8) & 0xF); + set => Operations = (ushort)((Operations & 0xF0FF) | ((int)value << 8)); + } + + public MTLCompareFunction StencilCompareFunction + { + readonly get => (MTLCompareFunction)((Operations >> 12) & 0xF); + set => Operations = (ushort)((Operations & 0x0FFF) | ((int)value << 12)); + } + } + + + [StructLayout(LayoutKind.Explicit, Size = 24)] + internal struct DepthStencilUid : IEquatable + { + [FieldOffset(0)] + public StencilUid FrontFace; + + [FieldOffset(10)] + public ushort DepthState; + + [FieldOffset(12)] + public StencilUid BackFace; + + [FieldOffset(22)] + private readonly ushort _padding; + + // Quick access aliases +#pragma warning disable IDE0044 // Add readonly modifier + [FieldOffset(0)] + private ulong _id0; + [FieldOffset(8)] + private ulong _id1; + [FieldOffset(0)] + private Vector128 _id01; + [FieldOffset(16)] + private ulong _id2; +#pragma warning restore IDE0044 // Add readonly modifier + + public MTLCompareFunction DepthCompareFunction + { + readonly get => (MTLCompareFunction)((DepthState >> 0) & 0xF); + set => DepthState = (ushort)((DepthState & 0xFFF0) | ((int)value << 0)); + } + + public bool StencilTestEnabled + { + readonly get => ((DepthState >> 4) & 0x1) != 0; + set => DepthState = (ushort)((DepthState & 0xFFEF) | ((value ? 1 : 0) << 4)); + } + + public bool DepthWriteEnabled + { + readonly get => ((DepthState >> 15) & 0x1) != 0; + set => DepthState = (ushort)((DepthState & 0x7FFF) | ((value ? 1 : 0) << 15)); + } + + public readonly override bool Equals(object obj) + { + return obj is DepthStencilUid other && EqualsRef(ref other); + } + + public readonly bool EqualsRef(ref DepthStencilUid other) + { + return _id01.Equals(other._id01) && _id2 == other._id2; + } + + public readonly bool Equals(DepthStencilUid other) + { + return EqualsRef(ref other); + } + + public readonly override int GetHashCode() + { + ulong hash64 = _id0 * 23 ^ + _id1 * 23 ^ + _id2 * 23; + + return (int)hash64 ^ ((int)(hash64 >> 32) * 17); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs new file mode 100644 index 000000000..c6e548c95 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs @@ -0,0 +1,338 @@ +using Ryujinx.Common.Logging; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + struct PipelineState + { + public PipelineUid Internal; + + public uint StagesCount + { + readonly get => (byte)((Internal.Id0 >> 0) & 0xFF); + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0); + } + + public uint VertexAttributeDescriptionsCount + { + readonly get => (byte)((Internal.Id0 >> 8) & 0xFF); + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8); + } + + public uint VertexBindingDescriptionsCount + { + readonly get => (byte)((Internal.Id0 >> 16) & 0xFF); + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16); + } + + public uint ColorBlendAttachmentStateCount + { + readonly get => (byte)((Internal.Id0 >> 24) & 0xFF); + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24); + } + + /* + * Can be an input to a pipeline, but not sure what the situation for that is. + public PrimitiveTopology Topology + { + readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF); + set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16); + } + */ + + // Reserved for when API is available. + public int LogicOp + { + readonly get => (int)((Internal.Id0 >> 32) & 0xF); + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32); + } + + //? + public bool PrimitiveRestartEnable + { + readonly get => ((Internal.Id0 >> 36) & 0x1) != 0UL; + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFEFFFFFFFFF) | ((value ? 1UL : 0UL) << 36); + } + + public bool RasterizerDiscardEnable + { + readonly get => ((Internal.Id0 >> 37) & 0x1) != 0UL; + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFDFFFFFFFFF) | ((value ? 1UL : 0UL) << 37); + } + + // Reserved for when API is available. + public bool LogicOpEnable + { + readonly get => ((Internal.Id0 >> 38) & 0x1) != 0UL; + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFBFFFFFFFFF) | ((value ? 1UL : 0UL) << 38); + } + + public bool AlphaToCoverageEnable + { + readonly get => ((Internal.Id0 >> 40) & 0x1) != 0UL; + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFEFFFFFFFFFF) | ((value ? 1UL : 0UL) << 40); + } + + public bool AlphaToOneEnable + { + readonly get => ((Internal.Id0 >> 41) & 0x1) != 0UL; + set => Internal.Id0 = (Internal.Id0 & 0xFFFFFDFFFFFFFFFF) | ((value ? 1UL : 0UL) << 41); + } + + public MTLPixelFormat DepthStencilFormat + { + readonly get => (MTLPixelFormat)(Internal.Id0 >> 48); + set => Internal.Id0 = (Internal.Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48); + } + + // Not sure how to appropriately use this, but it does need to be passed for tess. + public uint PatchControlPoints + { + readonly get => (uint)((Internal.Id1 >> 0) & 0xFFFFFFFF); + set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)value << 0); + } + + public uint SamplesCount + { + readonly get => (uint)((Internal.Id1 >> 32) & 0xFFFFFFFF); + set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)value << 32); + } + + // Advanced blend not supported + + private struct RenderPipelineDescriptorResult : IDisposable + { + public MTLRenderPipelineDescriptor Pipeline; + private MTLVertexDescriptor _vertex; + + public RenderPipelineDescriptorResult(MTLRenderPipelineDescriptor pipeline, MTLVertexDescriptor vertex) + { + Pipeline = pipeline; + _vertex = vertex; + } + + public void Dispose() + { + Pipeline.Dispose(); + _vertex.Dispose(); + } + } + + private readonly void BuildColorAttachment(MTLRenderPipelineColorAttachmentDescriptor descriptor, ColorBlendStateUid blendState) + { + descriptor.PixelFormat = blendState.PixelFormat; + descriptor.SetBlendingEnabled(blendState.Enable); + descriptor.AlphaBlendOperation = blendState.AlphaBlendOperation; + descriptor.RgbBlendOperation = blendState.RgbBlendOperation; + descriptor.SourceAlphaBlendFactor = blendState.SourceAlphaBlendFactor; + descriptor.DestinationAlphaBlendFactor = blendState.DestinationAlphaBlendFactor; + descriptor.SourceRGBBlendFactor = blendState.SourceRGBBlendFactor; + descriptor.DestinationRGBBlendFactor = blendState.DestinationRGBBlendFactor; + descriptor.WriteMask = blendState.WriteMask; + } + + private readonly MTLVertexDescriptor BuildVertexDescriptor() + { + var vertexDescriptor = new MTLVertexDescriptor(); + + for (int i = 0; i < VertexAttributeDescriptionsCount; i++) + { + VertexInputAttributeUid uid = Internal.VertexAttributes[i]; + + var attrib = vertexDescriptor.Attributes.Object((ulong)i); + attrib.Format = uid.Format; + attrib.Offset = uid.Offset; + attrib.BufferIndex = uid.BufferIndex; + } + + for (int i = 0; i < VertexBindingDescriptionsCount; i++) + { + VertexInputLayoutUid uid = Internal.VertexBindings[i]; + + var layout = vertexDescriptor.Layouts.Object((ulong)i); + + layout.StepFunction = uid.StepFunction; + layout.StepRate = uid.StepRate; + layout.Stride = uid.Stride; + } + + return vertexDescriptor; + } + + private RenderPipelineDescriptorResult CreateRenderDescriptor(Program program) + { + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + + for (int i = 0; i < Constants.MaxColorAttachments; i++) + { + var blendState = Internal.ColorBlendState[i]; + + if (blendState.PixelFormat != MTLPixelFormat.Invalid) + { + var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); + + BuildColorAttachment(pipelineAttachment, blendState); + } + } + + MTLPixelFormat dsFormat = DepthStencilFormat; + if (dsFormat != MTLPixelFormat.Invalid) + { + switch (dsFormat) + { + // Depth Only Attachment + case MTLPixelFormat.Depth16Unorm: + case MTLPixelFormat.Depth32Float: + renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat; + break; + + // Stencil Only Attachment + case MTLPixelFormat.Stencil8: + renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat; + break; + + // Combined Attachment + case MTLPixelFormat.Depth24UnormStencil8: + case MTLPixelFormat.Depth32FloatStencil8: + renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat; + renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat; + break; + default: + Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {dsFormat}!"); + break; + } + } + + /* TODO: enable when sharpmetal fixes the bindings + renderPipelineDescriptor.AlphaToCoverageEnabled = AlphaToCoverageEnable; + renderPipelineDescriptor.AlphaToOneEnabled = AlphaToOneEnable; + renderPipelineDescriptor.RasterizationEnabled = !RasterizerDiscardEnable; + */ + + renderPipelineDescriptor.SampleCount = Math.Max(1, SamplesCount); + + var vertexDescriptor = BuildVertexDescriptor(); + renderPipelineDescriptor.VertexDescriptor = vertexDescriptor; + + renderPipelineDescriptor.VertexFunction = program.VertexFunction; + + if (program.FragmentFunction.NativePtr != 0) + { + renderPipelineDescriptor.FragmentFunction = program.FragmentFunction; + } + + return new RenderPipelineDescriptorResult(renderPipelineDescriptor, vertexDescriptor); + } + + public MTLRenderPipelineState CreateRenderPipeline(MTLDevice device, Program program) + { + if (program.TryGetGraphicsPipeline(ref Internal, out var pipelineState)) + { + return pipelineState; + } + + using RenderPipelineDescriptorResult descriptors = CreateRenderDescriptor(program); + + var error = new NSError(IntPtr.Zero); + pipelineState = device.NewRenderPipelineState(descriptors.Pipeline, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + program.AddGraphicsPipeline(ref Internal, pipelineState); + + return pipelineState; + } + + public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program) + { + if (program.TryGetComputePipeline(out var pipelineState)) + { + return pipelineState; + } + + var error = new NSError(IntPtr.Zero); + pipelineState = device.NewComputePipelineState(program.ComputeFunction, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + program.AddComputePipeline(pipelineState); + + return pipelineState; + } + + public void Initialize() + { + SamplesCount = 1; + + Internal.ResetColorState(); + } + + /* + * TODO, this is from vulkan. + + private void UpdateVertexAttributeDescriptions(VulkanRenderer gd) + { + // Vertex attributes exceeding the stride are invalid. + // In metal, they cause glitches with the vertex shader fetching incorrect values. + // To work around this, we reduce the format to something that doesn't exceed the stride if possible. + // The assumption is that the exceeding components are not actually accessed on the shader. + + for (int index = 0; index < VertexAttributeDescriptionsCount; index++) + { + var attribute = Internal.VertexAttributeDescriptions[index]; + int vbIndex = GetVertexBufferIndex(attribute.Binding); + + if (vbIndex >= 0) + { + ref var vb = ref Internal.VertexBindingDescriptions[vbIndex]; + + Format format = attribute.Format; + + while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride) + { + Format newFormat = FormatTable.DropLastComponent(format); + + if (newFormat == format) + { + // That case means we failed to find a format that fits within the stride, + // so just restore the original format and give up. + format = attribute.Format; + break; + } + + format = newFormat; + } + + if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format)) + { + attribute.Format = format; + } + } + + _vertexAttributeDescriptions2[index] = attribute; + } + } + + private int GetVertexBufferIndex(uint binding) + { + for (int index = 0; index < VertexBindingDescriptionsCount; index++) + { + if (Internal.VertexBindingDescriptions[index].Binding == binding) + { + return index; + } + } + + return -1; + } + */ + } +} diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs b/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs new file mode 100644 index 000000000..4e2784b42 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs @@ -0,0 +1,200 @@ +using Ryujinx.Common.Memory; +using SharpMetal.Metal; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + struct VertexInputAttributeUid + { + public ulong Id0; + + public ulong Offset + { + readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF); + set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0); + } + + public MTLVertexFormat Format + { + readonly get => (MTLVertexFormat)((Id0 >> 32) & 0xFFFF); + set => Id0 = (Id0 & 0xFFFF0000FFFFFFFF) | ((ulong)value << 32); + } + + public ulong BufferIndex + { + readonly get => ((Id0 >> 48) & 0xFFFF); + set => Id0 = (Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48); + } + } + + struct VertexInputLayoutUid + { + public ulong Id0; + + public uint Stride + { + readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF); + set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0); + } + + public uint StepRate + { + readonly get => (uint)((Id0 >> 32) & 0x1FFFFFFF); + set => Id0 = (Id0 & 0xE0000000FFFFFFFF) | ((ulong)value << 32); + } + + public MTLVertexStepFunction StepFunction + { + readonly get => (MTLVertexStepFunction)((Id0 >> 61) & 0x7); + set => Id0 = (Id0 & 0x1FFFFFFFFFFFFFFF) | ((ulong)value << 61); + } + } + + struct ColorBlendStateUid + { + public ulong Id0; + + public MTLPixelFormat PixelFormat + { + readonly get => (MTLPixelFormat)((Id0 >> 0) & 0xFFFF); + set => Id0 = (Id0 & 0xFFFFFFFFFFFF0000) | ((ulong)value << 0); + } + + public MTLBlendFactor SourceRGBBlendFactor + { + readonly get => (MTLBlendFactor)((Id0 >> 16) & 0xFF); + set => Id0 = (Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16); + } + + public MTLBlendFactor DestinationRGBBlendFactor + { + readonly get => (MTLBlendFactor)((Id0 >> 24) & 0xFF); + set => Id0 = (Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24); + } + + public MTLBlendOperation RgbBlendOperation + { + readonly get => (MTLBlendOperation)((Id0 >> 32) & 0xF); + set => Id0 = (Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32); + } + + public MTLBlendOperation AlphaBlendOperation + { + readonly get => (MTLBlendOperation)((Id0 >> 36) & 0xF); + set => Id0 = (Id0 & 0xFFFFFF0FFFFFFFFF) | ((ulong)value << 36); + } + + public MTLBlendFactor SourceAlphaBlendFactor + { + readonly get => (MTLBlendFactor)((Id0 >> 40) & 0xFF); + set => Id0 = (Id0 & 0xFFFF00FFFFFFFFFF) | ((ulong)value << 40); + } + + public MTLBlendFactor DestinationAlphaBlendFactor + { + readonly get => (MTLBlendFactor)((Id0 >> 48) & 0xFF); + set => Id0 = (Id0 & 0xFF00FFFFFFFFFFFF) | ((ulong)value << 48); + } + + public MTLColorWriteMask WriteMask + { + readonly get => (MTLColorWriteMask)((Id0 >> 56) & 0xF); + set => Id0 = (Id0 & 0xF0FFFFFFFFFFFFFF) | ((ulong)value << 56); + } + + public bool Enable + { + readonly get => ((Id0 >> 63) & 0x1) != 0UL; + set => Id0 = (Id0 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); + } + } + + [SupportedOSPlatform("macos")] + struct PipelineUid : IRefEquatable + { + public ulong Id0; + public ulong Id1; + + private readonly uint VertexAttributeDescriptionsCount => (byte)((Id0 >> 8) & 0xFF); + private readonly uint VertexBindingDescriptionsCount => (byte)((Id0 >> 16) & 0xFF); + private readonly uint ColorBlendAttachmentStateCount => (byte)((Id0 >> 24) & 0xFF); + + public Array32 VertexAttributes; + public Array33 VertexBindings; + public Array8 ColorBlendState; + public uint AttachmentIntegerFormatMask; + public bool LogicOpsAllowed; + + public void ResetColorState() + { + ColorBlendState = new(); + + for (int i = 0; i < ColorBlendState.Length; i++) + { + ColorBlendState[i].WriteMask = MTLColorWriteMask.All; + } + } + + public readonly override bool Equals(object obj) + { + return obj is PipelineUid other && Equals(other); + } + + public bool Equals(ref PipelineUid other) + { + if (!Unsafe.As>(ref Id0).Equals(Unsafe.As>(ref other.Id0))) + { + return false; + } + + if (!SequenceEqual(VertexAttributes.AsSpan(), other.VertexAttributes.AsSpan(), VertexAttributeDescriptionsCount)) + { + return false; + } + + if (!SequenceEqual(VertexBindings.AsSpan(), other.VertexBindings.AsSpan(), VertexBindingDescriptionsCount)) + { + return false; + } + + if (!SequenceEqual(ColorBlendState.AsSpan(), other.ColorBlendState.AsSpan(), ColorBlendAttachmentStateCount)) + { + return false; + } + + return true; + } + + private static bool SequenceEqual(ReadOnlySpan x, ReadOnlySpan y, uint count) where T : unmanaged + { + return MemoryMarshal.Cast(x[..(int)count]).SequenceEqual(MemoryMarshal.Cast(y[..(int)count])); + } + + public override int GetHashCode() + { + ulong hash64 = Id0 * 23 ^ + Id1 * 23; + + for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++) + { + hash64 ^= VertexAttributes[i].Id0 * 23; + } + + for (int i = 0; i < (int)VertexBindingDescriptionsCount; i++) + { + hash64 ^= VertexBindings[i].Id0 * 23; + } + + for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++) + { + hash64 ^= ColorBlendState[i].Id0 * 23; + } + + return (int)hash64 ^ ((int)(hash64 >> 32) * 17); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 57e446ce6..668ddd8be 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -12,9 +12,11 @@ namespace Ryujinx.Graphics.Metal { public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) { + MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); + var descriptor = new MTLTextureDescriptor { - PixelFormat = FormatTable.GetFormat(Info.Format), + PixelFormat = pixelFormat, Usage = MTLTextureUsage.Unknown, SampleCount = (ulong)Info.Samples, TextureType = Info.Target.Convert(), @@ -35,6 +37,7 @@ namespace Ryujinx.Graphics.Metal descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat); _mtlTexture = _device.NewTexture(descriptor); + MtlFormat = pixelFormat; } public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info) @@ -51,6 +54,7 @@ namespace Ryujinx.Graphics.Metal var swizzle = GetSwizzle(info, pixelFormat); _mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + MtlFormat = pixelFormat; } private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index 51f5ec8d2..96daf8d3b 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Metal public int Width => Info.Width; public int Height => Info.Height; public int Depth => Info.Depth; + public MTLPixelFormat MtlFormat { get; protected set; } public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) { -- 2.47.1 From 4b53d18bef7fa1db44c6e860e6d8b982d435066e Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 29 Jun 2024 19:07:07 +0100 Subject: [PATCH 264/368] Fix Geometry/TFB on compute, Buffer Textures, add Window Resizing (#28) --- src/Ryujinx.Graphics.GAL/ComputeSize.cs | 18 +++++ src/Ryujinx.Graphics.GAL/Format.cs | 78 +++++++++++++++++++ src/Ryujinx.Graphics.GAL/IPipeline.cs | 2 +- .../Commands/DispatchComputeCommand.cs | 10 +-- .../Multithreading/ThreadedPipeline.cs | 4 +- src/Ryujinx.Graphics.GAL/ShaderInfo.cs | 17 ++-- .../Engine/Compute/ComputeClass.cs | 2 +- .../Threed/ComputeDraw/VtgAsComputeContext.cs | 2 +- .../Threed/ComputeDraw/VtgAsComputeState.cs | 10 +-- .../Shader/DiskCache/DiskCacheHostStorage.cs | 3 +- .../DiskCache/ParallelDiskCacheLoader.cs | 7 +- .../Shader/GpuChannelComputeState.cs | 11 +++ .../Shader/ShaderCache.cs | 5 +- .../Shader/ShaderInfoBuilder.cs | 27 ++++--- .../EncoderStateManager.cs | 10 ++- src/Ryujinx.Graphics.Metal/HelperShader.cs | 4 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 4 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 11 +-- src/Ryujinx.Graphics.Metal/Program.cs | 5 +- .../State/PipelineState.cs | 26 ++++++- src/Ryujinx.Graphics.Metal/Texture.cs | 2 + src/Ryujinx.Graphics.Metal/TextureBase.cs | 2 +- src/Ryujinx.Graphics.Metal/TextureBuffer.cs | 67 +++++++++++----- src/Ryujinx.Graphics.Metal/Window.cs | 25 +++++- src/Ryujinx.Graphics.OpenGL/Pipeline.cs | 2 +- .../CodeGen/Msl/Declarations.cs | 2 +- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 12 +-- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 +- 28 files changed, 280 insertions(+), 92 deletions(-) create mode 100644 src/Ryujinx.Graphics.GAL/ComputeSize.cs diff --git a/src/Ryujinx.Graphics.GAL/ComputeSize.cs b/src/Ryujinx.Graphics.GAL/ComputeSize.cs new file mode 100644 index 000000000..ce9c2531c --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ComputeSize.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct ComputeSize + { + public readonly static ComputeSize VtgAsCompute = new ComputeSize(32, 32, 1); + + public readonly int X; + public readonly int Y; + public readonly int Z; + + public ComputeSize(int x, int y, int z) + { + X = x; + Y = y; + Z = z; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index 17c42d2d4..0eeae8e26 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -339,6 +339,84 @@ namespace Ryujinx.Graphics.GAL return 1; } + /// + /// Get bytes per element for this format. + /// + /// Texture format + /// Byte size for an element of this format (pixel, vertex attribute, etc) + public static int GetBytesPerElement(this Format format) + { + int scalarSize = format.GetScalarSize(); + + switch (format) + { + case Format.R8G8Unorm: + case Format.R8G8Snorm: + case Format.R8G8Uint: + case Format.R8G8Sint: + case Format.R8G8Uscaled: + case Format.R8G8Sscaled: + case Format.R16G16Float: + case Format.R16G16Unorm: + case Format.R16G16Snorm: + case Format.R16G16Uint: + case Format.R16G16Sint: + case Format.R16G16Uscaled: + case Format.R16G16Sscaled: + case Format.R32G32Float: + case Format.R32G32Uint: + case Format.R32G32Sint: + case Format.R32G32Uscaled: + case Format.R32G32Sscaled: + return 2 * scalarSize; + + case Format.R8G8B8Unorm: + case Format.R8G8B8Snorm: + case Format.R8G8B8Uint: + case Format.R8G8B8Sint: + case Format.R8G8B8Uscaled: + case Format.R8G8B8Sscaled: + case Format.R16G16B16Float: + case Format.R16G16B16Unorm: + case Format.R16G16B16Snorm: + case Format.R16G16B16Uint: + case Format.R16G16B16Sint: + case Format.R16G16B16Uscaled: + case Format.R16G16B16Sscaled: + case Format.R32G32B32Float: + case Format.R32G32B32Uint: + case Format.R32G32B32Sint: + case Format.R32G32B32Uscaled: + case Format.R32G32B32Sscaled: + return 3 * scalarSize; + + case Format.R8G8B8A8Unorm: + case Format.R8G8B8A8Snorm: + case Format.R8G8B8A8Uint: + case Format.R8G8B8A8Sint: + case Format.R8G8B8A8Srgb: + case Format.R8G8B8A8Uscaled: + case Format.R8G8B8A8Sscaled: + case Format.B8G8R8A8Unorm: + case Format.B8G8R8A8Srgb: + case Format.R16G16B16A16Float: + case Format.R16G16B16A16Unorm: + case Format.R16G16B16A16Snorm: + case Format.R16G16B16A16Uint: + case Format.R16G16B16A16Sint: + case Format.R16G16B16A16Uscaled: + case Format.R16G16B16A16Sscaled: + case Format.R32G32B32A32Float: + case Format.R32G32B32A32Uint: + case Format.R32G32B32A32Sint: + case Format.R32G32B32A32Uscaled: + case Format.R32G32B32A32Sscaled: + return 4 * scalarSize; + } + + return scalarSize; + } + /// /// Checks if the texture format is a depth or depth-stencil format. /// diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs index 08533ceaa..b8409a573 100644 --- a/src/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.GAL void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size); - void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ); + void DispatchCompute(int groupsX, int groupsY, int groupsZ); void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance); void DrawIndexed( diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs index 36e0d836a..65028378f 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs @@ -6,23 +6,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands private int _groupsX; private int _groupsY; private int _groupsZ; - private int _groupSizeX; - private int _groupSizeY; - private int _groupSizeZ; - public void Set(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void Set(int groupsX, int groupsY, int groupsZ) { _groupsX = groupsX; _groupsY = groupsY; _groupsZ = groupsZ; - _groupSizeX = groupSizeX; - _groupSizeY = groupSizeY; - _groupSizeZ = groupSizeZ; } public static void Run(ref DispatchComputeCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.DispatchCompute(command._groupsX, command._groupsY, command._groupsZ, command._groupSizeX, command._groupSizeY, command._groupSizeZ); + renderer.Pipeline.DispatchCompute(command._groupsX, command._groupsY, command._groupsZ); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 509954faf..deec36648 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -63,9 +63,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { - _renderer.New().Set(groupsX, groupsY, groupsZ, groupSizeX, groupSizeY, groupSizeZ); + _renderer.New().Set(groupsX, groupsY, groupsZ); _renderer.QueueCommand(); } diff --git a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs index 2fd3227dc..c7965a03d 100644 --- a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs +++ b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs @@ -4,23 +4,22 @@ namespace Ryujinx.Graphics.GAL { public int FragmentOutputMap { get; } public ResourceLayout ResourceLayout { get; } + public ComputeSize ComputeLocalSize { get; } public ProgramPipelineState? State { get; } public bool FromCache { get; set; } - public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false) + public ShaderInfo( + int fragmentOutputMap, + ResourceLayout resourceLayout, + ComputeSize computeLocalSize, + ProgramPipelineState? state, + bool fromCache = false) { FragmentOutputMap = fragmentOutputMap; ResourceLayout = resourceLayout; + ComputeLocalSize = computeLocalSize; State = state; FromCache = fromCache; } - - public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false) - { - FragmentOutputMap = fragmentOutputMap; - ResourceLayout = resourceLayout; - State = null; - FromCache = fromCache; - } } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index 98c0ffa20..cd8144724 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute _channel.BufferManager.CommitComputeBindings(); - _context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth, qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2); + _context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth); _3dEngine.ForceShaderUpdate(); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs index 6de50fb2e..6dba27a7d 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw 1, 1, 1, - 1, + format.GetBytesPerElement(), format, DepthStencilMode.Depth, Target.TextureBuffer, diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 16ae83e6f..73682866b 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -211,10 +211,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.Pipeline.DispatchCompute( BitUtils.DivRoundUp(_count, ComputeLocalSize), BitUtils.DivRoundUp(_instanceCount, ComputeLocalSize), - 1, - ComputeLocalSize, - ComputeLocalSize, - ComputeLocalSize); + 1); } /// @@ -263,10 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.Pipeline.DispatchCompute( BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize), BitUtils.DivRoundUp(_instanceCount, ComputeLocalSize), - _geometryAsCompute.Info.ThreadsPerInputPrimitive, - ComputeLocalSize, - ComputeLocalSize, - ComputeLocalSize); + _geometryAsCompute.Info.ThreadsPerInputPrimitive); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index c36fc0ada..e621177d6 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -392,7 +392,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache context, shaders, specState.PipelineState, - specState.TransformFeedbackDescriptors != null); + specState.TransformFeedbackDescriptors != null, + specState.ComputeState.GetLocalSize()); IProgram hostProgram; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 20f96462e..74922d1e3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -490,7 +490,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null); + ref GpuChannelComputeState computeState = ref compilation.SpecializationState.ComputeState; + + ShaderInfoBuilder shaderInfoBuilder = new( + _context, + compilation.SpecializationState.TransformFeedbackDescriptors != null, + computeLocalSize: computeState.GetLocalSize()); for (int index = 0; index < compilation.TranslatedStages.Length; index++) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs index d8cdbc348..720f7e796 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs @@ -1,3 +1,5 @@ +using Ryujinx.Graphics.GAL; + namespace Ryujinx.Graphics.Gpu.Shader { /// @@ -61,5 +63,14 @@ namespace Ryujinx.Graphics.Gpu.Shader SharedMemorySize = sharedMemorySize; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; } + + /// + /// Gets the local group size of the shader in a GAL compatible struct. + /// + /// Local group size + public ComputeSize GetLocalSize() + { + return new ComputeSize(LocalSizeX, LocalSizeY, LocalSizeZ); + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index c67c6a2d6..64ea7c979 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -224,7 +224,10 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false); ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) }; - ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info); + ShaderInfo info = ShaderInfoBuilder.BuildForCompute( + _context, + translatedShader.Program.Info, + computeState.GetLocalSize()); IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info); cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader); diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 49823562f..54a03f43b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceStages.Geometry; private readonly GpuContext _context; + private readonly ComputeSize _computeLocalSize; private int _fragmentOutputMap; @@ -39,9 +40,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// GPU context that owns the shaders that will be added to the builder /// Indicates if the graphics shader is used with transform feedback enabled /// Indicates that the vertex shader will be emulated on a compute shader - public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false) + /// Indicates the local thread size for a compute shader + public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false, ComputeSize computeLocalSize = default) { _context = context; + _computeLocalSize = computeLocalSize; _fragmentOutputMap = -1; @@ -361,14 +364,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly()); - if (pipeline.HasValue) - { - return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache); - } - else - { - return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache); - } + return new ShaderInfo(_fragmentOutputMap, resourceLayout, _computeLocalSize, pipeline, fromCache); } /// @@ -378,14 +374,16 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shaders from the disk cache /// Optional pipeline for background compilation /// Indicates if the graphics shader is used with transform feedback enabled + /// Compute local thread size /// Shader information public static ShaderInfo BuildForCache( GpuContext context, IEnumerable programs, ProgramPipelineState? pipeline, - bool tfEnabled) + bool tfEnabled, + ComputeSize computeLocalSize) { - ShaderInfoBuilder builder = new(context, tfEnabled); + ShaderInfoBuilder builder = new(context, tfEnabled, computeLocalSize: computeLocalSize); foreach (CachedShaderStage program in programs) { @@ -403,11 +401,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU context that owns the shader /// Compute shader information + /// Compute local thread size /// True if the compute shader comes from a disk cache, false otherwise /// Shader information - public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false) + public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, ComputeSize computeLocalSize, bool fromCache = false) { - ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false); + ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false, computeLocalSize: computeLocalSize); builder.AddStageInfo(info); @@ -424,7 +423,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shader information public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false) { - ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true); + ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute); builder.AddStageInfo(info, vertexAsCompute: true); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 62c965697..db0e8ffa7 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.Metal public readonly PrimitiveTopology Topology => _currentState.Topology; public readonly Texture[] RenderTargets => _currentState.RenderTargets; public readonly Texture DepthStencil => _currentState.DepthStencil; + public readonly ComputeSize ComputeLocalSize => _currentState.ComputeProgram.ComputeLocalSize; // RGBA32F is the biggest format private const int ZeroBufferSize = 4 * 4; @@ -811,6 +812,7 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= {Constants.MaxTexturesPerStage}"); return; } + switch (stage) { case ShaderStage.Fragment: @@ -852,10 +854,14 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, MTLSamplerState sampler) + public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) { UpdateTexture(stage, binding, texture); - UpdateSampler(stage, binding, sampler); + + if (sampler != null) + { + UpdateSampler(stage, binding, sampler.GetSampler()); + } } private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index ec944b0f8..5525186f6 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Metal _programStrideChange = new Program( [ new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) - ], device); + ], device, new ComputeSize(64, 1, 1)); } private static string ReadMsl(string fileName) @@ -260,7 +260,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programStrideChange); - _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 64, 1, 1); + _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1); // Restore previous state _pipeline.SwapState(null); diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index c68da5a4a..a0d6faced 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Metal public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { - return new Program(shaders, _device); + return new Program(shaders, _device, info.ComputeLocalSize); } public ISampler CreateSampler(SamplerCreateInfo info) @@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Metal { if (info.Target == Target.TextureBuffer) { - return new TextureBuffer(this, info); + return new TextureBuffer(_device, this, _pipeline, info); } return new Texture(_device, this, _pipeline, info); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 6363eb5d8..f410c789c 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -347,13 +347,15 @@ namespace Ryujinx.Graphics.Metal BufferHolder.Copy(this, Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { var computeCommandEncoder = GetOrCreateComputeEncoder(true); + ComputeSize localSize = _encoderStateManager.ComputeLocalSize; + computeCommandEncoder.DispatchThreadgroups( new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ }, - new MTLSize { width = (ulong)groupSizeX, height = (ulong)groupSizeY, depth = (ulong)groupSizeZ }); + new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z }); } public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) @@ -658,12 +660,11 @@ namespace Ryujinx.Graphics.Metal { if (texture is TextureBase tex) { - if (sampler is Sampler samp) + if (sampler == null || sampler is Sampler) { - var mtlSampler = samp.GetSampler(); var index = (ulong)binding; - _encoderStateManager.UpdateTextureAndSampler(stage, index, tex, mtlSampler); + _encoderStateManager.UpdateTextureAndSampler(stage, index, tex, (Sampler)sampler); } } } diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 40cb6df77..5635b711c 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -15,13 +15,16 @@ namespace Ryujinx.Graphics.Metal public MTLFunction VertexFunction; public MTLFunction FragmentFunction; public MTLFunction ComputeFunction; + public ComputeSize ComputeLocalSize { get; } private HashTableSlim _graphicsPipelineCache; private MTLComputePipelineState? _computePipelineCache; private bool _firstBackgroundUse; - public Program(ShaderSource[] shaders, MTLDevice device) + public Program(ShaderSource[] shaders, MTLDevice device, ComputeSize computeLocalSize = default) { + ComputeLocalSize = computeLocalSize; + for (int index = 0; index < shaders.Length; index++) { ShaderSource shader = shaders[index]; diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs index c6e548c95..fa6d5410b 100644 --- a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs +++ b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; using SharpMetal.Foundation; using SharpMetal.Metal; using System; @@ -249,6 +250,27 @@ namespace Ryujinx.Graphics.Metal return pipelineState; } + public static MTLComputePipelineDescriptor CreateComputeDescriptor(Program program) + { + ComputeSize localSize = program.ComputeLocalSize; + + uint maxThreads = (uint)(localSize.X * localSize.Y * localSize.Z); + + if (maxThreads == 0) + { + throw new InvalidOperationException($"Local thread size for compute cannot be 0 in any dimension."); + } + + var descriptor = new MTLComputePipelineDescriptor + { + ComputeFunction = program.ComputeFunction, + MaxTotalThreadsPerThreadgroup = maxThreads, + ThreadGroupSizeIsMultipleOfThreadExecutionWidth = true, + }; + + return descriptor; + } + public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program) { if (program.TryGetComputePipeline(out var pipelineState)) @@ -256,8 +278,10 @@ namespace Ryujinx.Graphics.Metal return pipelineState; } + using MTLComputePipelineDescriptor descriptor = CreateComputeDescriptor(program); + var error = new NSError(IntPtr.Zero); - pipelineState = device.NewComputePipelineState(program.ComputeFunction, ref error); + pipelineState = device.NewComputePipelineState(descriptor, MTLPipelineOption.None, 0, ref error); if (error != IntPtr.Zero) { Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 668ddd8be..fdff81f0d 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -37,7 +37,9 @@ namespace Ryujinx.Graphics.Metal descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat); _mtlTexture = _device.NewTexture(descriptor); + MtlFormat = pixelFormat; + descriptor.Dispose(); } public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info) diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index 96daf8d3b..fcd07a66a 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Metal return _mtlTexture; } - public void Release() + public virtual void Release() { Dispose(); } diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs index 3db1e7c4a..033e12105 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs @@ -7,27 +7,54 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class TextureBuffer : ITexture + class TextureBuffer : TextureBase, ITexture { - private readonly MetalRenderer _renderer; - + private MTLTextureDescriptor _descriptor; private BufferHandle _bufferHandle; private int _offset; private int _size; private int _bufferCount; - public int Width { get; } - public int Height { get; } - - public MTLPixelFormat MtlFormat { get; } - - public TextureBuffer(MetalRenderer renderer, TextureCreateInfo info) + public TextureBuffer(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) { - _renderer = renderer; - Width = info.Width; - Height = info.Height; - MtlFormat = FormatTable.GetFormat(info.Format); + MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); + + _descriptor = new MTLTextureDescriptor + { + PixelFormat = pixelFormat, + Usage = MTLTextureUsage.Unknown, + TextureType = MTLTextureType.TextureBuffer, + Width = (ulong)Info.Width, + Height = (ulong)Info.Height, + }; + + MtlFormat = pixelFormat; + } + + private void RebuildStorage() + { + // Find the parent buffer, and try to build a texture from it. + + // TODO: texture uses should register read/write usage on the assigned buffer. + Auto bufferAuto = _renderer.BufferManager.GetBuffer(_bufferHandle, false); + + if (_mtlTexture.NativePtr != 0) + { + _mtlTexture.Dispose(); + } + + if (bufferAuto == null) + { + _mtlTexture = default; + } + else + { + DisposableBuffer buffer = bufferAuto.Get(_pipeline.Cbs, _offset, _size); + + _descriptor.Width = (uint)(_size / Info.BytesPerPixel); + _mtlTexture = buffer.Value.NewTexture(_descriptor, (ulong)_offset, (ulong)_size); + } } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) @@ -65,11 +92,6 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void Release() - { - - } - public void SetData(IMemoryOwner data) { _renderer.SetBufferData(_bufferHandle, _offset, data.Memory.Span); @@ -101,7 +123,14 @@ namespace Ryujinx.Graphics.Metal _size = buffer.Size; _bufferCount = _renderer.BufferManager.BufferCount; - Release(); + RebuildStorage(); + } + + public override void Release() + { + _descriptor.Dispose(); + + base.Release(); } } } diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 38ee6459b..6489b591d 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -18,6 +18,10 @@ namespace Ryujinx.Graphics.Metal private int _width; private int _height; + + private int _requestedWidth; + private int _requestedHeight; + // private bool _vsyncEnabled; private AntiAliasing _currentAntiAliasing; private bool _updateEffect; @@ -35,10 +39,26 @@ namespace Ryujinx.Graphics.Metal _metalLayer = metalLayer; } - public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + private unsafe void ResizeIfNeeded() + { + if (_requestedWidth != 0 && _requestedHeight != 0) + { + // TODO: This is actually a CGSize, but there is no overload for that, so fill the first two fields of rect with the size. + var rect = new NSRect(_requestedWidth, _requestedHeight, 0, 0); + + ObjectiveC.objc_msgSend(_metalLayer, "setDrawableSize:", rect); + + _requestedWidth = 0; + _requestedHeight = 0; + } + } + + public unsafe void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) { + ResizeIfNeeded(); + var drawable = new CAMetalDrawable(ObjectiveC.IntPtr_objc_msgSend(_metalLayer, "nextDrawable")); _width = (int)drawable.Texture.Width; @@ -114,7 +134,8 @@ namespace Ryujinx.Graphics.Metal public void SetSize(int width, int height) { - // Ignore + _requestedWidth = width; + _requestedHeight = height; } public void ChangeVSyncMode(bool vsyncEnabled) diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index 4df9bdc19..096e2e5eb 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.OpenGL Buffer.Copy(source, destination, srcOffset, dstOffset, size); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { if (!_program.IsLinked) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 3179c80a2..5fac994b3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -241,7 +241,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl var textureTypeName = texture.Type.ToMslTextureType(); argBufferPointers[texture.Binding] = $"{textureTypeName} tex_{texture.Name};"; - if (!texture.Separate) + if (!texture.Separate && texture.Type != SamplerType.TextureBuffer) { argBufferPointers[Defaults.MaxTexturesPerStage + texture.Binding] = $"sampler samp_{texture.Name};"; } diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index 0243dda40..a5599dbe7 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common; +using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; @@ -861,7 +861,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programStrideChange); - _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 0, 0, 0); + _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1); _pipeline.Finish(gd, cbs); } @@ -1044,7 +1044,7 @@ namespace Ryujinx.Graphics.Vulkan int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32; - _pipeline.DispatchCompute(dispatchX, dispatchY, 1, 0, 0, 0); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); if (srcView != src) { @@ -1170,7 +1170,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(format)); - _pipeline.DispatchCompute(dispatchX, dispatchY, 1, 0, 0, 0); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); if (srcView != src) { @@ -1582,7 +1582,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(3, patternScoped.Range) }); _pipeline.SetProgram(_programConvertIndirectData); - _pipeline.DispatchCompute(1, 1, 1, 0, 0, 0); + _pipeline.DispatchCompute(1, 1, 1); BufferHolder.InsertBufferBarrier( gd, @@ -1684,7 +1684,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programConvertD32S8ToD24S8); - _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1, 0, 0, 0); + _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1); _pipeline.Finish(gd, cbs); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index c74c87e8d..5af0c3539 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; @@ -295,7 +295,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { if (!_program.IsLinked) { -- 2.47.1 From 2860db198cbd7917de2ea7425350aaeba56034ad Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jun 2024 22:54:28 +0100 Subject: [PATCH 265/368] Stop depth/stencil blits from crashing everything --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 13 +++++++++++-- src/Ryujinx.Graphics.Metal/Texture.cs | 5 ++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index f410c789c..44608e208 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -254,14 +254,23 @@ namespace Ryujinx.Graphics.Metal _renderer.RegisterFlush(); } - public void BlitColor( + public void Blit( ITexture src, ITexture dst, Extents2D srcRegion, Extents2D dstRegion, + bool isDepthOrStencil, bool linearFilter) { - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); + if (isDepthOrStencil) + { + // TODO: Depth & stencil blit! + Logger.Warning?.PrintMsg(LogClass.Gpu, "Requested a depth or stencil blit!"); + } + else + { + _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); + } } public void Barrier() diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index fdff81f0d..9c4fed5c9 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -162,7 +162,10 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { - _pipeline.BlitColor(this, destination, srcRegion, dstRegion, linearFilter); + var dst = (Texture)destination; + bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + + _pipeline.Blit(this, destination, srcRegion, dstRegion, isDepthOrStencil, linearFilter); } public void CopyTo(BufferRange range, int layer, int level, int stride) -- 2.47.1 From 7d5b4c5d1dd6b46b6e92b2ca1a07c558bdc0a266 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 30 Jun 2024 12:01:24 +0100 Subject: [PATCH 266/368] Dont bind images in texture slots --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 44608e208..7f11ecded 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -560,10 +560,7 @@ namespace Ryujinx.Graphics.Metal public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) { - if (texture is TextureBase tex) - { - _encoderStateManager.UpdateTexture(stage, (ulong)binding, tex); - } + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } public void SetImageArray(ShaderStage stage, int binding, IImageArray array) -- 2.47.1 From 879c93cf7384618f81de6c3eefe1f3b0be6c2039 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 30 Jun 2024 17:23:53 +0100 Subject: [PATCH 267/368] Preload command speedup, Texture/buffer data flush, blit shader fix (#30) * Move encoder state to be tied to command buffer, so preload and background cbs have their own encoder state * Texture buffer/data flush, blit shader fix --- .../BackgroundResources.cs | 1 + src/Ryujinx.Graphics.Metal/BufferHolder.cs | 12 +- src/Ryujinx.Graphics.Metal/BufferManager.cs | 6 +- .../CommandBufferEncoder.cs | 170 ++++++++++++++++ .../CommandBufferPool.cs | 30 ++- .../CommandBufferScoped.cs | 4 +- .../EncoderStateManager.cs | 24 +-- src/Ryujinx.Graphics.Metal/HelperShader.cs | 8 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 7 +- .../PersistentFlushBuffer.cs | 37 +++- src/Ryujinx.Graphics.Metal/Pipeline.cs | 118 +++-------- src/Ryujinx.Graphics.Metal/StagingBuffer.cs | 14 +- src/Ryujinx.Graphics.Metal/Texture.cs | 187 ++++++++++++------ 13 files changed, 414 insertions(+), 204 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs diff --git a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs index f02fd7205..ea49ac6ec 100644 --- a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs +++ b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs @@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Metal { MTLCommandQueue queue = _renderer.BackgroundQueue; _pool = new CommandBufferPool(queue); + _pool.Initialize(null); // TODO: Proper encoder factory for background render/compute } return _pool; diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs index f07143a43..e0089322f 100644 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Metal/BufferHolder.cs @@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Metal throw new InvalidOperationException("The buffer is not mapped."); } - public unsafe void SetData(int offset, ReadOnlySpan data, CommandBufferScoped? cbs = null, Action endRenderPass = null, bool allowCbsWait = true) + public unsafe void SetData(int offset, ReadOnlySpan data, CommandBufferScoped? cbs = null, bool allowCbsWait = true) { int dataSize = Math.Min(data.Length, Size - offset); if (dataSize == 0) @@ -199,12 +199,11 @@ namespace Ryujinx.Graphics.Metal // This avoids ending and beginning render passes on each buffer data upload. cbs = _pipeline.PreloadCbs; - endRenderPass = null; } if (allowCbsWait) { - _renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, endRenderPass, this, offset, data); + _renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, this, offset, data); } else { @@ -214,7 +213,7 @@ namespace Ryujinx.Graphics.Metal cbs = _renderer.CommandBufferPool.Rent(); } - if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, endRenderPass, this, offset, data)) + if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, this, offset, data)) { // Need to do a slow upload. BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize); @@ -223,7 +222,7 @@ namespace Ryujinx.Graphics.Metal var srcBuffer = srcHolder.GetBuffer(); var dstBuffer = this.GetBuffer(true); - Copy(_pipeline, cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize); + Copy(cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize); srcHolder.Dispose(); } @@ -255,7 +254,6 @@ namespace Ryujinx.Graphics.Metal } public static void Copy( - Pipeline pipeline, CommandBufferScoped cbs, Auto src, Auto dst, @@ -267,7 +265,7 @@ namespace Ryujinx.Graphics.Metal var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value; var dstbuffer = dst.Get(cbs, dstOffset, size, true).Value; - pipeline.GetOrCreateBlitEncoder().CopyFromBuffer( + cbs.Encoders.EnsureBlitEncoder().CopyFromBuffer( srcBuffer, (ulong)srcOffset, dstbuffer, diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs index 76d6d4fb8..28b6b2e24 100644 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -176,14 +176,14 @@ namespace Ryujinx.Graphics.Metal public void SetData(BufferHandle handle, int offset, ReadOnlySpan data) where T : unmanaged { - SetData(handle, offset, MemoryMarshal.Cast(data), null, null); + SetData(handle, offset, MemoryMarshal.Cast(data), null); } - public void SetData(BufferHandle handle, int offset, ReadOnlySpan data, CommandBufferScoped? cbs, Action endRenderPass) + public void SetData(BufferHandle handle, int offset, ReadOnlySpan data, CommandBufferScoped? cbs) { if (TryGetBuffer(handle, out var holder)) { - holder.SetData(offset, data, cbs, endRenderPass); + holder.SetData(offset, data, cbs); } } diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs new file mode 100644 index 000000000..9e7dc73ea --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs @@ -0,0 +1,170 @@ +using Ryujinx.Graphics.Metal; +using SharpMetal.Metal; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +interface IEncoderFactory +{ + MTLRenderCommandEncoder CreateRenderCommandEncoder(); + MTLComputeCommandEncoder CreateComputeCommandEncoder(); +} + +/// +/// Tracks active encoder object for a command buffer. +/// +[SupportedOSPlatform("macos")] +class CommandBufferEncoder +{ + public EncoderType CurrentEncoderType { get; private set; } = EncoderType.None; + + public MTLBlitCommandEncoder BlitEncoder => new MTLBlitCommandEncoder(CurrentEncoder.Value); + + public MTLComputeCommandEncoder ComputeEncoder => new MTLComputeCommandEncoder(CurrentEncoder.Value); + + public MTLRenderCommandEncoder RenderEncoder => new MTLRenderCommandEncoder(CurrentEncoder.Value); + + internal MTLCommandEncoder? CurrentEncoder { get; private set; } + + private MTLCommandBuffer _commandBuffer; + private IEncoderFactory _encoderFactory; + + public void Initialize(MTLCommandBuffer commandBuffer, IEncoderFactory encoderFactory) + { + _commandBuffer = commandBuffer; + _encoderFactory = encoderFactory; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MTLRenderCommandEncoder EnsureRenderEncoder() + { + if (CurrentEncoderType != EncoderType.Render) + { + return BeginRenderPass(); + } + + return RenderEncoder; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MTLBlitCommandEncoder EnsureBlitEncoder() + { + if (CurrentEncoderType != EncoderType.Blit) + { + return BeginBlitPass(); + } + + return BlitEncoder; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MTLComputeCommandEncoder EnsureComputeEncoder() + { + if (CurrentEncoderType != EncoderType.Compute) + { + return BeginComputePass(); + } + + return ComputeEncoder; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetRenderEncoder(out MTLRenderCommandEncoder encoder) + { + if (CurrentEncoderType != EncoderType.Render) + { + encoder = default; + return false; + } + + encoder = RenderEncoder; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetBlitEncoder(out MTLBlitCommandEncoder encoder) + { + if (CurrentEncoderType != EncoderType.Blit) + { + encoder = default; + return false; + } + + encoder = BlitEncoder; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetComputeEncoder(out MTLComputeCommandEncoder encoder) + { + if (CurrentEncoderType != EncoderType.Compute) + { + encoder = default; + return false; + } + + encoder = ComputeEncoder; + return true; + } + + public void EndCurrentPass() + { + if (CurrentEncoder != null) + { + switch (CurrentEncoderType) + { + case EncoderType.Blit: + BlitEncoder.EndEncoding(); + CurrentEncoder = null; + break; + case EncoderType.Compute: + ComputeEncoder.EndEncoding(); + CurrentEncoder = null; + break; + case EncoderType.Render: + RenderEncoder.EndEncoding(); + CurrentEncoder = null; + break; + default: + throw new InvalidOperationException(); + } + + CurrentEncoderType = EncoderType.None; + } + } + + private MTLRenderCommandEncoder BeginRenderPass() + { + EndCurrentPass(); + + var renderCommandEncoder = _encoderFactory.CreateRenderCommandEncoder(); + + CurrentEncoder = renderCommandEncoder; + CurrentEncoderType = EncoderType.Render; + + return renderCommandEncoder; + } + + private MTLBlitCommandEncoder BeginBlitPass() + { + EndCurrentPass(); + + var descriptor = new MTLBlitPassDescriptor(); + var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); + + CurrentEncoder = blitCommandEncoder; + CurrentEncoderType = EncoderType.Blit; + return blitCommandEncoder; + } + + private MTLComputeCommandEncoder BeginComputePass() + { + EndCurrentPass(); + + var computeCommandEncoder = _encoderFactory.CreateComputeCommandEncoder(); + + CurrentEncoder = computeCommandEncoder; + CurrentEncoderType = EncoderType.Compute; + return computeCommandEncoder; + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs index ac8c45b20..9c9e452fb 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Versioning; +using System.Threading; namespace Ryujinx.Graphics.Metal { @@ -14,6 +15,10 @@ namespace Ryujinx.Graphics.Metal private readonly int _totalCommandBuffers; private readonly int _totalCommandBuffersMask; private readonly MTLCommandQueue _queue; + private readonly Thread _owner; + private IEncoderFactory _defaultEncoderFactory; + + public bool OwnedByCurrentThread => _owner == Thread.CurrentThread; [SupportedOSPlatform("macos")] private struct ReservedCommandBuffer @@ -22,22 +27,28 @@ namespace Ryujinx.Graphics.Metal public bool InConsumption; public int SubmissionCount; public MTLCommandBuffer CommandBuffer; + public CommandBufferEncoder Encoders; public FenceHolder Fence; public List Dependants; public List Waitables; - public void Reinitialize(MTLCommandQueue queue) + public void Reinitialize(MTLCommandQueue queue, IEncoderFactory stateManager) { CommandBuffer = queue.CommandBuffer(); + + Encoders.Initialize(CommandBuffer, stateManager); } - public void Initialize(MTLCommandQueue queue) + public void Initialize(MTLCommandQueue queue, IEncoderFactory stateManager) { CommandBuffer = queue.CommandBuffer(); Dependants = new List(); Waitables = new List(); + Encoders = new CommandBufferEncoder(); + + Encoders.Initialize(CommandBuffer, stateManager); } } @@ -51,6 +62,7 @@ namespace Ryujinx.Graphics.Metal public CommandBufferPool(MTLCommandQueue queue) { _queue = queue; + _owner = Thread.CurrentThread; _totalCommandBuffers = MaxCommandBuffers; _totalCommandBuffersMask = _totalCommandBuffers - 1; @@ -60,10 +72,15 @@ namespace Ryujinx.Graphics.Metal _queuedIndexes = new int[_totalCommandBuffers]; _queuedIndexesPtr = 0; _queuedCount = 0; + } + + public void Initialize(IEncoderFactory encoderFactory) + { + _defaultEncoderFactory = encoderFactory; for (int i = 0; i < _totalCommandBuffers; i++) { - _commandBuffers[i].Initialize(_queue); + _commandBuffers[i].Initialize(_queue, _defaultEncoderFactory); WaitAndDecrementRef(i); } } @@ -194,7 +211,7 @@ namespace Ryujinx.Graphics.Metal _inUseCount++; - return new CommandBufferScoped(this, entry.CommandBuffer, cursor); + return new CommandBufferScoped(this, entry.CommandBuffer, entry.Encoders, cursor); } cursor = (cursor + 1) & _totalCommandBuffersMask; @@ -206,6 +223,9 @@ namespace Ryujinx.Graphics.Metal public void Return(CommandBufferScoped cbs) { + // Ensure the encoder is committed. + cbs.Encoders.EndCurrentPass(); + lock (_commandBuffers) { int cbIndex = cbs.CommandBufferIndex; @@ -223,7 +243,7 @@ namespace Ryujinx.Graphics.Metal commandBuffer.Commit(); // Replace entry with new MTLCommandBuffer - entry.Reinitialize(_queue); + entry.Reinitialize(_queue, _defaultEncoderFactory); int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers; _queuedIndexes[ptr] = cbIndex; diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs b/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs index 43cea6fe9..822f69b46 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs @@ -9,12 +9,14 @@ namespace Ryujinx.Graphics.Metal { private readonly CommandBufferPool _pool; public MTLCommandBuffer CommandBuffer { get; } + public CommandBufferEncoder Encoders { get; } public int CommandBufferIndex { get; } - public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, int commandBufferIndex) + public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, CommandBufferEncoder encoders, int commandBufferIndex) { _pool = pool; CommandBuffer = commandBuffer; + Encoders = encoders; CommandBufferIndex = commandBufferIndex; } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index db0e8ffa7..7699ed8f6 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -581,9 +581,8 @@ namespace Ryujinx.Graphics.Metal _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetDepthClamp(renderCommandEncoder); return; } @@ -600,9 +599,8 @@ namespace Ryujinx.Graphics.Metal _currentState.Clamp = clamp; // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetDepthBias(renderCommandEncoder); return; } @@ -632,9 +630,8 @@ namespace Ryujinx.Graphics.Metal } // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetScissors(renderCommandEncoder); return; } @@ -669,9 +666,8 @@ namespace Ryujinx.Graphics.Metal } // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetViewports(renderCommandEncoder); return; } @@ -688,9 +684,8 @@ namespace Ryujinx.Graphics.Metal UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); return; } @@ -755,9 +750,8 @@ namespace Ryujinx.Graphics.Metal _currentState.CullBoth = face == Face.FrontAndBack; // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetCullMode(renderCommandEncoder); SetScissors(renderCommandEncoder); return; @@ -778,9 +772,8 @@ namespace Ryujinx.Graphics.Metal _currentState.Winding = frontFace.Convert(); // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetFrontFace(renderCommandEncoder); return; } @@ -795,9 +788,8 @@ namespace Ryujinx.Graphics.Metal _currentState.BackRefValue = backRef; // Inline update - if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetStencilRefValue(renderCommandEncoder); } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 5525186f6..54ba9889d 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -277,7 +277,7 @@ namespace Ryujinx.Graphics.Metal DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors); // Save current state - EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags); + EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false); // Inherit some state without fully recreating render pipeline. RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, false, index); @@ -312,7 +312,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.Draw(4, 1, 0, 0); // Restore previous state - _pipeline.SwapState(null, clearFlags); + _pipeline.SwapState(null, clearFlags, false); _helperShaderState.Restore(save); } @@ -330,7 +330,7 @@ namespace Ryujinx.Graphics.Metal var helperScissors = _helperShaderState.Scissors; // Save current state - EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags); + EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false); // Inherit some state without fully recreating render pipeline. RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, true); @@ -365,7 +365,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); // Restore previous state - _pipeline.SwapState(null, clearFlags); + _pipeline.SwapState(null, clearFlags, false); _helperShaderState.Restore(save); } diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index a0d6faced..1edd91c56 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -67,7 +67,10 @@ namespace Ryujinx.Graphics.Metal public void BackgroundContextAction(Action action, bool alwaysBackground = false) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // GetData methods should be thread safe, so we can call this directly. + // Texture copy (scaled) may also happen in here, so that should also be thread safe. + + action(); } public BufferHandle CreateBuffer(int size, BufferAccess access) @@ -221,7 +224,7 @@ namespace Ryujinx.Graphics.Metal public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) { - BufferManager.SetData(buffer, offset, data, _pipeline.Cbs, _pipeline.EndRenderPassDelegate); + BufferManager.SetData(buffer, offset, data, _pipeline.Cbs); } public void UpdateCounters() diff --git a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs b/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs index 6b51d4af5..a1834f0b7 100644 --- a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.GAL; using System; using System.Runtime.Versioning; @@ -44,7 +45,7 @@ namespace Ryujinx.Graphics.Metal if (srcBuffer.TryIncrementReferenceCount()) { - BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false); + BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false); } else { @@ -58,6 +59,40 @@ namespace Ryujinx.Graphics.Metal return flushStorage.GetDataStorage(0, size); } + public Span GetTextureData(CommandBufferPool cbp, Texture view, int size) + { + TextureCreateInfo info = view.Info; + + var flushStorage = ResizeIfNeeded(size); + + using (var cbs = cbp.Rent()) + { + var buffer = flushStorage.GetBuffer().Get(cbs).Value; + var image = view.GetHandle(); + + view.CopyFromOrToBuffer(cbs, buffer, image, size, true, 0, 0, info.GetLayers(), info.Levels, singleSlice: false); + } + + flushStorage.WaitForFences(); + return flushStorage.GetDataStorage(0, size); + } + + public Span GetTextureData(CommandBufferPool cbp, Texture view, int size, int layer, int level) + { + var flushStorage = ResizeIfNeeded(size); + + using (var cbs = cbp.Rent()) + { + var buffer = flushStorage.GetBuffer().Get(cbs).Value; + var image = view.GetHandle(); + + view.CopyFromOrToBuffer(cbs, buffer, image, size, true, layer, level, 1, 1, singleSlice: true); + } + + flushStorage.WaitForFences(); + return flushStorage.GetDataStorage(0, size); + } + public void Dispose() { _flushStorage.Dispose(); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 7f11ecded..3e17dde41 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - class Pipeline : IPipeline, IDisposable + class Pipeline : IPipeline, IEncoderFactory, IDisposable { private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB @@ -27,7 +27,6 @@ namespace Ryujinx.Graphics.Metal private EncoderStateManager _encoderStateManager; private ulong _byteWeight; - public readonly Action EndRenderPassDelegate; public MTLCommandBuffer CommandBuffer; public IndexBufferPattern QuadsToTrisPattern; @@ -35,8 +34,8 @@ namespace Ryujinx.Graphics.Metal internal CommandBufferScoped? PreloadCbs { get; private set; } internal CommandBufferScoped Cbs { get; private set; } - internal MTLCommandEncoder? CurrentEncoder { get; private set; } - internal EncoderType CurrentEncoderType { get; private set; } = EncoderType.None; + internal CommandBufferEncoder Encoders => Cbs.Encoders; + internal EncoderType CurrentEncoderType => Encoders.CurrentEncoderType; internal bool RenderPassActive { get; private set; } public Pipeline(MTLDevice device, MetalRenderer renderer) @@ -44,7 +43,7 @@ namespace Ryujinx.Graphics.Metal _device = device; _renderer = renderer; - EndRenderPassDelegate = EndCurrentPass; + renderer.CommandBufferPool.Initialize(this); CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer; } @@ -57,8 +56,13 @@ namespace Ryujinx.Graphics.Metal TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true); } - public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All) + public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All, bool endRenderPass = true) { + if (endRenderPass && CurrentEncoderType == EncoderType.Render) + { + EndCurrentPass(); + } + return _encoderStateManager.SwapState(state, flags); } @@ -79,15 +83,7 @@ namespace Ryujinx.Graphics.Metal public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false) { - MTLRenderCommandEncoder renderCommandEncoder; - if (CurrentEncoder == null || CurrentEncoderType != EncoderType.Render) - { - renderCommandEncoder = BeginRenderPass(); - } - else - { - renderCommandEncoder = new MTLRenderCommandEncoder(CurrentEncoder.Value); - } + MTLRenderCommandEncoder renderCommandEncoder = Cbs.Encoders.EnsureRenderEncoder(); if (forDraw) { @@ -99,28 +95,12 @@ namespace Ryujinx.Graphics.Metal public MTLBlitCommandEncoder GetOrCreateBlitEncoder() { - if (CurrentEncoder != null) - { - if (CurrentEncoderType == EncoderType.Blit) - { - return new MTLBlitCommandEncoder(CurrentEncoder.Value); - } - } - - return BeginBlitPass(); + return Cbs.Encoders.EnsureBlitEncoder(); } public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false) { - MTLComputeCommandEncoder computeCommandEncoder; - if (CurrentEncoder == null || CurrentEncoderType != EncoderType.Compute) - { - computeCommandEncoder = BeginComputePass(); - } - else - { - computeCommandEncoder = new MTLComputeCommandEncoder(CurrentEncoder.Value); - } + MTLComputeCommandEncoder computeCommandEncoder = Cbs.Encoders.EnsureComputeEncoder(); if (forDispatch) { @@ -132,65 +112,17 @@ namespace Ryujinx.Graphics.Metal public void EndCurrentPass() { - if (CurrentEncoder != null) - { - switch (CurrentEncoderType) - { - case EncoderType.Blit: - new MTLBlitCommandEncoder(CurrentEncoder.Value).EndEncoding(); - CurrentEncoder = null; - break; - case EncoderType.Compute: - new MTLComputeCommandEncoder(CurrentEncoder.Value).EndEncoding(); - CurrentEncoder = null; - break; - case EncoderType.Render: - new MTLRenderCommandEncoder(CurrentEncoder.Value).EndEncoding(); - CurrentEncoder = null; - RenderPassActive = false; - break; - default: - throw new ArgumentOutOfRangeException(); - } - - CurrentEncoderType = EncoderType.None; - } + Cbs.Encoders.EndCurrentPass(); } - private MTLRenderCommandEncoder BeginRenderPass() + public MTLRenderCommandEncoder CreateRenderCommandEncoder() { - EndCurrentPass(); - - var renderCommandEncoder = _encoderStateManager.CreateRenderCommandEncoder(); - - CurrentEncoder = renderCommandEncoder; - CurrentEncoderType = EncoderType.Render; - RenderPassActive = true; - - return renderCommandEncoder; + return _encoderStateManager.CreateRenderCommandEncoder(); } - private MTLBlitCommandEncoder BeginBlitPass() + public MTLComputeCommandEncoder CreateComputeCommandEncoder() { - EndCurrentPass(); - - var descriptor = new MTLBlitPassDescriptor(); - var blitCommandEncoder = Cbs.CommandBuffer.BlitCommandEncoder(descriptor); - - CurrentEncoder = blitCommandEncoder; - CurrentEncoderType = EncoderType.Blit; - return blitCommandEncoder; - } - - private MTLComputeCommandEncoder BeginComputePass() - { - EndCurrentPass(); - - var computeCommandEncoder = _encoderStateManager.CreateComputeCommandEncoder(); - - CurrentEncoder = computeCommandEncoder; - CurrentEncoderType = EncoderType.Compute; - return computeCommandEncoder; + return _encoderStateManager.CreateComputeCommandEncoder(); } public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear) @@ -279,19 +211,15 @@ namespace Ryujinx.Graphics.Metal { case EncoderType.Render: { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment; - renderCommandEncoder.MemoryBarrier(scope, stages, stages); + Encoders.RenderEncoder.MemoryBarrier(scope, stages, stages); break; } case EncoderType.Compute: { - var computeCommandEncoder = GetOrCreateComputeEncoder(); - var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;; - computeCommandEncoder.MemoryBarrier(scope); + Encoders.ComputeEncoder.MemoryBarrier(scope); break; } } @@ -353,7 +281,7 @@ namespace Ryujinx.Graphics.Metal var srcBuffer = _renderer.BufferManager.GetBuffer(src, srcOffset, size, false); var dstBuffer = _renderer.BufferManager.GetBuffer(dst, dstOffset, size, true); - BufferHolder.Copy(this, Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size); + BufferHolder.Copy(Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size); } public void DispatchCompute(int groupsX, int groupsY, int groupsZ) @@ -709,9 +637,7 @@ namespace Ryujinx.Graphics.Metal { if (CurrentEncoderType == EncoderType.Render) { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - - renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment); + Encoders.RenderEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment); } } diff --git a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs index 07450f6b0..d739cdd3f 100644 --- a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs @@ -63,15 +63,13 @@ namespace Ryujinx.Graphics.Metal _resourceAlignment = Constants.MinResourceAlignment; } - public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan data) + public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) { bool isRender = cbs != null; CommandBufferScoped scoped = cbs ?? cbp.Rent(); // Must push all data to the buffer. If it can't fit, split it up. - endRenderPass?.Invoke(); - while (data.Length > 0) { if (_freeSize < data.Length) @@ -122,14 +120,14 @@ namespace Ryujinx.Graphics.Metal _buffer.SetDataUnchecked(offset, data[..capacity]); _buffer.SetDataUnchecked(0, data[capacity..]); - BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity); - BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity); + BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity); + BufferHolder.Copy(cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity); } else { _buffer.SetDataUnchecked(offset, data); - BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length); + BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length); } _freeOffset = (offset + data.Length) & (BufferSize - 1); @@ -139,7 +137,7 @@ namespace Ryujinx.Graphics.Metal _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length)); } - public bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan data) + public bool TryPushData(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) { if (data.Length > BufferSize) { @@ -156,8 +154,6 @@ namespace Ryujinx.Graphics.Metal } } - endRenderPass?.Invoke(); - PushDataImpl(cbs, dst, dstOffset, data); return true; diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 9c4fed5c9..e938a04b8 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using SharpMetal.Foundation; using SharpMetal.Metal; @@ -94,6 +95,13 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { + if (!_renderer.CommandBufferPool.OwnedByCurrentThread) + { + Logger.Warning?.PrintMsg(LogClass.Gpu, "Metal doesn't currently support scaled blit on background thread."); + + return; + } + var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); if (destination is Texture destinationTexture) @@ -202,98 +210,157 @@ namespace Ryujinx.Graphics.Metal return new Texture(_device, _renderer, _pipeline, info, _mtlTexture, firstLayer, firstLevel); } - public PinnedSpan GetData() + private int GetBufferDataLength(int size) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + // TODO: D32S8 conversion - ulong length = 0; + return size; + } - for (int level = 0; level < Info.Levels; level++) + private ReadOnlySpan GetDataFromBuffer(ReadOnlySpan storage, int size, Span output) + { + // TODO: D32S8 conversion + + return storage; + } + + public void CopyFromOrToBuffer( + CommandBufferScoped cbs, + MTLBuffer buffer, + MTLTexture image, + int size, + bool to, + int dstLayer, + int dstLevel, + int dstLayers, + int dstLevels, + bool singleSlice, + int offset = 0, + int stride = 0) + { + MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); + + bool is3D = Info.Target == Target.Texture3D; + int width = Math.Max(1, Info.Width >> dstLevel); + int height = Math.Max(1, Info.Height >> dstLevel); + int depth = is3D && !singleSlice ? Math.Max(1, Info.Depth >> dstLevel) : 1; + int layers = dstLayers; + int levels = dstLevels; + + for (int oLevel = 0; oLevel < levels; oLevel++) { - length += (ulong)Info.GetMipSize(level); - } + int level = oLevel + dstLevel; + int mipSize = Info.GetMipSize2D(level); - unsafe - { - var mtlBuffer = _device.NewBuffer(length, MTLResourceOptions.ResourceStorageModeShared); + int mipSizeLevel = GetBufferDataLength(is3D && !singleSlice + ? Info.GetMipSize(level) + : mipSize * dstLayers); - int width = Info.Width; - int height = Info.Height; - int depth = Info.Depth; - int levels = Info.GetLevelsClamped(); - int layers = Info.GetLayers(); - bool is3D = Info.Target == Target.Texture3D; + int endOffset = offset + mipSizeLevel; - int offset = 0; - - for (int level = 0; level < levels; level++) + if ((uint)endOffset > (uint)size) { - int mipSize = Info.GetMipSize2D(level); - int endOffset = offset + mipSize; + break; + } - for (int layer = 0; layer < layers; layer++) + for (int oLayer = 0; oLayer < layers; oLayer++) + { + int layer = !is3D ? dstLayer + oLayer : 0; + int z = is3D ? dstLayer + oLayer : 0; + + if (to) { blitCommandEncoder.CopyFromTexture( - _mtlTexture, + image, (ulong)layer, (ulong)level, - new MTLOrigin(), - new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, - mtlBuffer, + new MTLOrigin { z = (ulong)z }, + new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 }, + buffer, (ulong)offset, (ulong)Info.GetMipStride(level), (ulong)mipSize ); - - offset += mipSize; } - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (is3D) + else { - depth = Math.Max(1, depth >> 1); + blitCommandEncoder.CopyFromBuffer( + buffer, + (ulong)offset, + (ulong)Info.GetMipStride(level), + (ulong)mipSize, + new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 }, + image, + (ulong)(layer + oLayer), + (ulong)level, + new MTLOrigin { z = (ulong)z } + ); } + + offset += mipSize; } - // TODO: wait + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); - return new PinnedSpan(mtlBuffer.Contents.ToPointer(), (int)length, () => mtlBuffer.Dispose()); + if (Info.Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } } } + private ReadOnlySpan GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer) + { + int size = 0; + + for (int level = 0; level < Info.Levels; level++) + { + size += Info.GetMipSize(level); + } + + size = GetBufferDataLength(size); + + Span result = flushBuffer.GetTextureData(cbp, this, size); + + return GetDataFromBuffer(result, size, result); + } + + private ReadOnlySpan GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer, int layer, int level) + { + int size = GetBufferDataLength(Info.GetMipSize(level)); + + Span result = flushBuffer.GetTextureData(cbp, this, size, layer, level); + + return GetDataFromBuffer(result, size, result); + } + + public PinnedSpan GetData() + { + BackgroundResource resources = _renderer.BackgroundResources.Get(); + + if (_renderer.CommandBufferPool.OwnedByCurrentThread) + { + _renderer.FlushAllCommands(); + + return PinnedSpan.UnsafeFromSpan(GetData(_renderer.CommandBufferPool, resources.GetFlushBuffer())); + } + + return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer())); + } + public PinnedSpan GetData(int layer, int level) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + BackgroundResource resources = _renderer.BackgroundResources.Get(); - ulong bytesPerRow = (ulong)Info.GetMipStride(level); - ulong length = bytesPerRow * (ulong)Info.Height; - ulong bytesPerImage = 0; - if (_mtlTexture.TextureType == MTLTextureType.Type3D) + if (_renderer.CommandBufferPool.OwnedByCurrentThread) { - bytesPerImage = length; + _renderer.FlushAllCommands(); + + return PinnedSpan.UnsafeFromSpan(GetData(_renderer.CommandBufferPool, resources.GetFlushBuffer(), layer, level)); } - unsafe - { - - var mtlBuffer = _device.NewBuffer(length, MTLResourceOptions.ResourceStorageModeShared); - - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin(), - new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage - ); - - return new PinnedSpan(mtlBuffer.Contents.ToPointer(), (int)length, () => mtlBuffer.Dispose()); - } + return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level)); } public void SetData(IMemoryOwner data) -- 2.47.1 From 2fb3c6975ef46bc67e0177cedf3fe90f15fe1a69 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 30 Jun 2024 19:04:28 +0100 Subject: [PATCH 268/368] Create command buffers when rented rather than in advance (#31) * Make it less likely to freeze, but the creation of the command buffer should probably be moved * Create command buffers as they're rented rather than in advance --- .../BackgroundResources.cs | 2 +- .../CommandBufferPool.cs | 35 ++++++------------- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs index ea49ac6ec..c06a6747b 100644 --- a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs +++ b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Metal if (_pool == null) { MTLCommandQueue queue = _renderer.BackgroundQueue; - _pool = new CommandBufferPool(queue); + _pool = new CommandBufferPool(queue, true); _pool.Initialize(null); // TODO: Proper encoder factory for background render/compute } diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs index 9c9e452fb..050f93efc 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs @@ -33,22 +33,21 @@ namespace Ryujinx.Graphics.Metal public List Dependants; public List Waitables; - public void Reinitialize(MTLCommandQueue queue, IEncoderFactory stateManager) + public void Use(MTLCommandQueue queue, IEncoderFactory stateManager) { CommandBuffer = queue.CommandBuffer(); + Fence = new FenceHolder(CommandBuffer); Encoders.Initialize(CommandBuffer, stateManager); + + InUse = true; } - public void Initialize(MTLCommandQueue queue, IEncoderFactory stateManager) + public void Initialize() { - CommandBuffer = queue.CommandBuffer(); - Dependants = new List(); Waitables = new List(); Encoders = new CommandBufferEncoder(); - - Encoders.Initialize(CommandBuffer, stateManager); } } @@ -59,12 +58,12 @@ namespace Ryujinx.Graphics.Metal private int _queuedCount; private int _inUseCount; - public CommandBufferPool(MTLCommandQueue queue) + public CommandBufferPool(MTLCommandQueue queue, bool isLight = false) { _queue = queue; _owner = Thread.CurrentThread; - _totalCommandBuffers = MaxCommandBuffers; + _totalCommandBuffers = isLight ? 2 : MaxCommandBuffers; _totalCommandBuffersMask = _totalCommandBuffers - 1; _commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers]; @@ -80,7 +79,7 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < _totalCommandBuffers; i++) { - _commandBuffers[i].Initialize(_queue, _defaultEncoderFactory); + _commandBuffers[i].Initialize(); WaitAndDecrementRef(i); } } @@ -207,7 +206,7 @@ namespace Ryujinx.Graphics.Metal if (!entry.InUse && !entry.InConsumption) { - entry.InUse = true; + entry.Use(_queue, _defaultEncoderFactory); _inUseCount++; @@ -242,16 +241,13 @@ namespace Ryujinx.Graphics.Metal var commandBuffer = entry.CommandBuffer; commandBuffer.Commit(); - // Replace entry with new MTLCommandBuffer - entry.Reinitialize(_queue, _defaultEncoderFactory); - int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers; _queuedIndexes[ptr] = cbIndex; _queuedCount++; } } - private void WaitAndDecrementRef(int cbIndex, bool refreshFence = true) + private void WaitAndDecrementRef(int cbIndex) { ref var entry = ref _commandBuffers[cbIndex]; @@ -275,22 +271,13 @@ namespace Ryujinx.Graphics.Metal entry.Dependants.Clear(); entry.Waitables.Clear(); entry.Fence?.Dispose(); - - if (refreshFence) - { - entry.Fence = new FenceHolder(entry.CommandBuffer); - } - else - { - entry.Fence = null; - } } public void Dispose() { for (int i = 0; i < _totalCommandBuffers; i++) { - WaitAndDecrementRef(i, refreshFence: false); + WaitAndDecrementRef(i); } } } diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 1edd91c56..d9ac0118c 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Metal throw new NotSupportedException("Metal backend requires Tier 2 Argument Buffer support."); } - _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers); + _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers + 1); BackgroundQueue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers); _getMetalLayer = metalLayer; -- 2.47.1 From 144397c3da3217fcad5b0d94881c7a912533535d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 1 Jul 2024 18:01:53 +0100 Subject: [PATCH 269/368] =?UTF-8?q?Don=E2=80=99t=20do=20inline=20vertex=20?= =?UTF-8?q?buffer=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Somehow broke zero buff MTLVertexDescriptor, but fixes broken geoemtry so I’m pushing anyway --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 11 +++++------ src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 15 ++------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 2f732681b..95f0f21b5 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -22,13 +22,12 @@ namespace Ryujinx.Graphics.Metal StencilRef = 1 << 7, Viewports = 1 << 8, Scissors = 1 << 9, - VertexBuffers = 1 << 10, - Buffers = 1 << 11, - VertexTextures = 1 << 12, - FragmentTextures = 1 << 13, - ComputeTextures = 1 << 14, + Buffers = 1 << 10, + VertexTextures = 1 << 11, + FragmentTextures = 1 << 12, + ComputeTextures = 1 << 13, - RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | VertexBuffers | Buffers | VertexTextures | FragmentTextures, + RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Buffers | VertexTextures | FragmentTextures, ComputeAll = ComputePipeline | Buffers | ComputeTextures, All = RenderAll | ComputeAll, } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 7699ed8f6..b76170068 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -179,6 +179,7 @@ namespace Ryujinx.Graphics.Metal { if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline)) { + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); SetRenderPipelineState(renderCommandEncoder); } @@ -222,11 +223,6 @@ namespace Ryujinx.Graphics.Metal SetScissors(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.VertexBuffers)) - { - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - } - if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) { SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); @@ -683,15 +679,8 @@ namespace Ryujinx.Graphics.Metal // Update the buffers on the pipeline UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - return; - } - // Mark dirty - _currentState.Dirty |= DirtyFlags.RenderPipeline | DirtyFlags.VertexBuffers; + _currentState.Dirty |= DirtyFlags.RenderPipeline; } public void UpdateUniformBuffers(ReadOnlySpan buffers) -- 2.47.1 From 549938e2b11beabb43d3618404180117cdb8cd38 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 1 Jul 2024 18:02:43 +0100 Subject: [PATCH 270/368] Update comment for Metal --- src/Ryujinx.Graphics.Metal/SyncManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/SyncManager.cs b/src/Ryujinx.Graphics.Metal/SyncManager.cs index 0ec508365..1d37bacc4 100644 --- a/src/Ryujinx.Graphics.Metal/SyncManager.cs +++ b/src/Ryujinx.Graphics.Metal/SyncManager.cs @@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Metal if (!signaled) { - Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); + Logger.Error?.PrintMsg(LogClass.Gpu, $"Metal Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); } else { -- 2.47.1 From 60722a1837718529c730a20724c92e94f8fadec8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:24:10 +0100 Subject: [PATCH 271/368] Metal: Better Bindings (#29) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Tell GAL to use Vk model (and break everything) * ResourceBindingSegments * Set information on backend caps * Get ready to break everything * Refactor EncoderStateManager * Remove padding from helper shaders * Fix ref array sizes * Seperate vert & frag buffers * Shader-side changes * Fixes * Fix some helper shader resource layouts * Sort by binding id * Fix helper shader layouts * Don’t do inline vertex buffer updates * Check for null storage --- .../Shader/GpuAccessorBase.cs | 8 +- src/Ryujinx.Graphics.Metal/Constants.cs | 6 +- src/Ryujinx.Graphics.Metal/EncoderState.cs | 41 +- .../EncoderStateManager.cs | 658 +++++++++++------- src/Ryujinx.Graphics.Metal/HelperShader.cs | 20 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 17 +- src/Ryujinx.Graphics.Metal/Program.cs | 157 ++++- .../ResourceBindingSegment.cs | 22 + .../ResourceLayoutBuilder.cs | 59 ++ src/Ryujinx.Graphics.Metal/Shaders/Blit.metal | 126 ---- .../Shaders/ChangeBufferStride.metal | 1 - .../CodeGen/Msl/Declarations.cs | 47 +- 12 files changed, 709 insertions(+), 453 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs create mode 100644 src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index d89eebabf..701ff764a 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api == TargetApi.Vulkan) + if (_context.Capabilities.Api != TargetApi.OpenGL) { binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer"); } @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api == TargetApi.Vulkan) + if (_context.Capabilities.Api != TargetApi.OpenGL) { if (count == 1) { @@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api == TargetApi.Vulkan) + if (_context.Capabilities.Api != TargetApi.OpenGL) { binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer"); } @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api == TargetApi.Vulkan) + if (_context.Capabilities.Api != TargetApi.OpenGL) { if (count == 1) { diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 032815359..1ee24e308 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Metal public const int MaxUniformBuffersPerStage = 18; public const int MaxStorageBuffersPerStage = 16; public const int MaxTexturesPerStage = 64; + public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages; + public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; public const int MaxColorAttachments = 8; // TODO: Check this value @@ -18,9 +20,11 @@ namespace Ryujinx.Graphics.Metal public const int MinResourceAlignment = 16; // Must match constants set in shader generation + public const uint ZeroBufferIndex = 18; + public const uint ConstantBuffersIndex = 20; public const uint StorageBuffersIndex = 21; - public const uint ZeroBufferIndex = 18; public const uint TexturesIndex = 22; + public const uint ImagessIndex = 23; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 95f0f21b5..448c9eaa2 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Metal.State; +using Ryujinx.Graphics.Shader; using SharpMetal.Metal; using System; using System.Linq; @@ -22,13 +23,13 @@ namespace Ryujinx.Graphics.Metal StencilRef = 1 << 7, Viewports = 1 << 8, Scissors = 1 << 9, - Buffers = 1 << 10, - VertexTextures = 1 << 11, - FragmentTextures = 1 << 12, - ComputeTextures = 1 << 13, + Uniforms = 1 << 10, + Storages = 1 << 11, + Textures = 1 << 12, + Images = 1 << 13, - RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Buffers | VertexTextures | FragmentTextures, - ComputeAll = ComputePipeline | Buffers | ComputeTextures, + RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Uniforms | Storages | Textures | Images, + ComputeAll = ComputePipeline | Uniforms | Storages | Textures | Images, All = RenderAll | ComputeAll, } @@ -49,6 +50,20 @@ namespace Ryujinx.Graphics.Metal } } + record struct TextureRef + { + public ShaderStage Stage; + public Texture Storage; + public Sampler Sampler; + + public TextureRef(ShaderStage stage, Texture storage, Sampler sampler) + { + Stage = stage; + Storage = storage; + Sampler = sampler; + } + } + struct PredrawState { public MTLCullMode CullMode; @@ -73,17 +88,9 @@ namespace Ryujinx.Graphics.Metal public PipelineState Pipeline; public DepthStencilUid DepthStencilUid; - public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage]; - public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - - public TextureBase[] VertexTextures = new TextureBase[Constants.MaxTexturesPerStage]; - public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - - public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTexturesPerStage]; - public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - - public BufferRef[] UniformBuffers = new BufferRef[Constants.MaxUniformBuffersPerStage]; - public BufferRef[] StorageBuffers = new BufferRef[Constants.MaxStorageBuffersPerStage]; + public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; + public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; + public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings]; public Auto IndexBuffer = default; public MTLIndexType IndexType = MTLIndexType.UInt16; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index b76170068..dd0502f23 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -179,8 +179,8 @@ namespace Ryujinx.Graphics.Metal { if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline)) { - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); SetRenderPipelineState(renderCommandEncoder); + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); } if (_currentState.Dirty.HasFlag(DirtyFlags.DepthStencil)) @@ -223,21 +223,26 @@ namespace Ryujinx.Graphics.Metal SetScissors(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms)) { - SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.UniformSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.VertexTextures)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Storages)) { - SetRenderTextures(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.StorageSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.FragmentTextures)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Textures)) { - SetRenderTextures(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.TextureSetIndex); } + // if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) + // { + // UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.ImageSetIndex); + // } + _currentState.Dirty &= ~DirtyFlags.RenderAll; } @@ -248,15 +253,27 @@ namespace Ryujinx.Graphics.Metal SetComputePipelineState(computeCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms)) { - SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.UniformSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.ComputeTextures)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Storages)) { - SetComputeTextures(computeCommandEncoder, _currentState.ComputeTextures, _currentState.ComputeSamplers); + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.StorageSetIndex); } + + if (_currentState.Dirty.HasFlag(DirtyFlags.Textures)) + { + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.TextureSetIndex); + } + + // if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) + // { + // UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.ImageSetIndex); + // } + + _currentState.Dirty &= ~DirtyFlags.ComputeAll; } private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) @@ -694,10 +711,10 @@ namespace Ryujinx.Graphics.Metal ? null : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - _currentState.UniformBuffers[index] = new BufferRef(mtlBuffer, ref buffer); + _currentState.UniformBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); } - _currentState.Dirty |= DirtyFlags.Buffers; + _currentState.Dirty |= DirtyFlags.Uniforms; } public void UpdateStorageBuffers(ReadOnlySpan buffers) @@ -711,10 +728,10 @@ namespace Ryujinx.Graphics.Metal ? null : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - _currentState.StorageBuffers[index] = new BufferRef(mtlBuffer, ref buffer); + _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); } - _currentState.Dirty |= DirtyFlags.Buffers; + _currentState.Dirty |= DirtyFlags.Storages; } public void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) @@ -724,10 +741,10 @@ namespace Ryujinx.Graphics.Metal var mtlBuffer = buffers[i]; int index = first + i; - _currentState.StorageBuffers[index] = new BufferRef(mtlBuffer); + _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer); } - _currentState.Dirty |= DirtyFlags.Buffers; + _currentState.Dirty |= DirtyFlags.Storages; } // Inlineable @@ -786,63 +803,22 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.StencilRef; } - public void UpdateTexture(ShaderStage stage, ulong binding, TextureBase texture) - { - if (binding > Constants.MaxTexturesPerStage) - { - Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= {Constants.MaxTexturesPerStage}"); - return; - } - - switch (stage) - { - case ShaderStage.Fragment: - _currentState.FragmentTextures[binding] = texture; - _currentState.Dirty |= DirtyFlags.FragmentTextures; - break; - case ShaderStage.Vertex: - _currentState.VertexTextures[binding] = texture; - _currentState.Dirty |= DirtyFlags.VertexTextures; - break; - case ShaderStage.Compute: - _currentState.ComputeTextures[binding] = texture; - _currentState.Dirty |= DirtyFlags.ComputeTextures; - break; - } - } - - public void UpdateSampler(ShaderStage stage, ulong binding, MTLSamplerState sampler) - { - if (binding > Constants.MaxTexturesPerStage) - { - Logger.Warning?.Print(LogClass.Gpu, $"Sampler binding ({binding}) must be <= {Constants.MaxTexturesPerStage}"); - return; - } - switch (stage) - { - case ShaderStage.Fragment: - _currentState.FragmentSamplers[binding] = sampler; - _currentState.Dirty |= DirtyFlags.FragmentTextures; - break; - case ShaderStage.Vertex: - _currentState.VertexSamplers[binding] = sampler; - _currentState.Dirty |= DirtyFlags.VertexTextures; - break; - case ShaderStage.Compute: - _currentState.ComputeSamplers[binding] = sampler; - _currentState.Dirty |= DirtyFlags.ComputeTextures; - break; - } - } - public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) { - UpdateTexture(stage, binding, texture); - - if (sampler != null) + if (texture is TextureBuffer textureBuffer) { - UpdateSampler(stage, binding, sampler.GetSampler()); + // TODO: Texture buffers } + else if (texture is Texture view) + { + _currentState.TextureRefs[binding] = new(stage, view, sampler); + } + else + { + _currentState.TextureRefs[binding] = default; + } + + _currentState.Dirty |= DirtyFlags.Textures; } private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) @@ -999,119 +975,373 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex); } - private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers) + private void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, int setIndex) { - var uniformArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, uniformBuffers, true); - var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value; + var bindingSegments = program.BindingSegments[setIndex]; - renderCommandEncoder.SetVertexBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); - renderCommandEncoder.SetFragmentBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); - - var storageArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, storageBuffers, false); - var storageArgBuffer = _bufferManager.GetBuffer(storageArgBufferRange.Handle, true).Get(_pipeline.Cbs).Value; - - renderCommandEncoder.SetVertexBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); - renderCommandEncoder.SetFragmentBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); - } - - private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers) - { - var uniformArgBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, uniformBuffers, true); - var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value; - - computeCommandEncoder.SetBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); - - - var storageArgBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, storageBuffers, false); - var storageArgBuffer = _bufferManager.GetBuffer(storageArgBufferRange.Handle, true).Get(_pipeline.Cbs).Value; - - computeCommandEncoder.SetBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); - } - - private readonly BufferRange CreateArgumentBufferForRenderEncoder(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] buffers, bool constant) - { - var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; - - Span resourceIds = stackalloc ulong[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) + if (bindingSegments.Length == 0) { - var range = buffers[i].Range; - var autoBuffer = buffers[i].Buffer; - var offset = 0; - - if (autoBuffer == null) - { - continue; - } - - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage, MTLRenderStages.RenderStageFragment | MTLRenderStages.RenderStageVertex); - resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset; + return; } - var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; + var vertArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); + var fragArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.FragArgumentBufferSizes[setIndex] * sizeof(ulong)); - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); + Span vertResourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; + Span fragResourceIds = stackalloc ulong[program.FragArgumentBufferSizes[setIndex]]; - return argBuffer.Range; - } + var vertResourceIdIndex = 0; + var fragResourceIdIndex = 0; - private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] buffers, bool constant) - { - var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; - - Span resourceIds = stackalloc ulong[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) + foreach (ResourceBindingSegment segment in bindingSegments) { - var range = buffers[i].Range; - var autoBuffer = buffers[i].Buffer; - var offset = 0; + int binding = segment.Binding; + int count = segment.Count; - if (autoBuffer == null) + switch (setIndex) { - continue; + case MetalRenderer.UniformSetIndex: + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + var offset = 0; + + if (autoBuffer == null) + { + continue; + } + + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + MTLRenderStages renderStages = 0; + + if (segment.Stages.HasFlag(ResourceStages.Vertex)) + { + vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + vertResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if (segment.Stages.HasFlag(ResourceStages.Fragment)) + { + fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + fragResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); + } + break; + case MetalRenderer.StorageSetIndex: + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + var offset = 0; + + if (autoBuffer == null) + { + continue; + } + + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + MTLRenderStages renderStages = 0; + + if (segment.Stages.HasFlag(ResourceStages.Vertex)) + { + vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + vertResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if (segment.Stages.HasFlag(ResourceStages.Fragment)) + { + fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + fragResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); + } + break; + case MetalRenderer.TextureSetIndex: + if (!segment.IsArray) + { + if (segment.Type != ResourceType.BufferTexture) + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref var texture = ref _currentState.TextureRefs[index]; + + var storage = texture.Storage; + + if (storage == null) + { + continue; + } + + var mtlTexture = storage.GetHandle(); + + MTLRenderStages renderStages = 0; + + if (segment.Stages.HasFlag(ResourceStages.Vertex)) + { + vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIdIndex++; + + if (texture.Sampler != null) + { + vertResourceIds[vertResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + vertResourceIdIndex++; + } + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if (segment.Stages.HasFlag(ResourceStages.Fragment)) + { + fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIdIndex++; + + if (texture.Sampler != null) + { + fragResourceIds[fragResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + fragResourceIdIndex++; + } + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages); + } + } + else + { + // TODO: Buffer textures + } + } + else + { + // TODO: Texture arrays + } + break; + case MetalRenderer.ImageSetIndex: + // TODO: Images + break; } - - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage); - resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset; } - var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; + vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds)); + fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds)); + + var mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; + var mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; + + renderCommandEncoder.SetVertexBuffer(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + renderCommandEncoder.SetFragmentBuffer(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + } + + private void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, int setIndex) + { + var bindingSegments = program.BindingSegments[setIndex]; + + if (bindingSegments.Length == 0) + { + return; + } + + var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); + Span resourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; + var resourceIdIndex = 0; + + foreach (ResourceBindingSegment segment in bindingSegments) + { + int binding = segment.Binding; + int count = segment.Count; + + switch (setIndex) + { + case MetalRenderer.UniformSetIndex: + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + var offset = 0; + + if (autoBuffer == null) + { + continue; + } + + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read); + resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + resourceIdIndex++; + } + } + break; + case MetalRenderer.StorageSetIndex: + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + var offset = 0; + + if (autoBuffer == null) + { + continue; + } + + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); + resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + resourceIdIndex++; + } + } + break; + case MetalRenderer.TextureSetIndex: + if (!segment.IsArray) + { + if (segment.Type != ResourceType.BufferTexture) + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref var texture = ref _currentState.TextureRefs[index]; + + var storage = texture.Storage; + + if (storage == null) + { + continue; + } + + var mtlTexture = storage.GetHandle(); + + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); + resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resourceIdIndex++; + + if (texture.Sampler != null) + { + resourceIds[resourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + resourceIdIndex++; + } + } + } + } + else + { + // TODO: Buffer textures + } + } + else + { + // TODO: Texture arrays + } + break; + case MetalRenderer.ImageSetIndex: + // TODO: Images + break; + } + } - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - return argBuffer.Range; + var mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value; + + computeCommandEncoder.SetBuffer(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); } + private uint SetIndexToBindingIndex(int setIndex) + { + return setIndex switch + { + MetalRenderer.UniformSetIndex => Constants.ConstantBuffersIndex, + MetalRenderer.StorageSetIndex => Constants.StorageBuffersIndex, + MetalRenderer.TextureSetIndex => Constants.TexturesIndex, + MetalRenderer.ImageSetIndex => Constants.ImagessIndex, + }; + } + + private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) { renderCommandEncoder.SetCullMode(_currentState.CullMode); @@ -1126,105 +1356,5 @@ namespace Ryujinx.Graphics.Metal { renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue); } - - private readonly void SetRenderTextures(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers) - { - var argBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, stage, textures, samplers); - var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value; - - switch (stage) - { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); - break; - } - } - - private readonly void SetComputeTextures(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers) - { - var argBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, textures, samplers); - var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value; - - computeCommandEncoder.SetBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); - } - - private readonly BufferRange CreateArgumentBufferForRenderEncoder(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers) - { - var renderStage = stage == ShaderStage.Vertex ? MTLRenderStages.RenderStageVertex : MTLRenderStages.RenderStageFragment; - - Span resourceIds = stackalloc ulong[textures.Length + samplers.Length]; - - for (int i = 0; i < textures.Length; i++) - { - if (textures[i] == null) - { - continue; - } - - var mtlTexture = textures[i].GetHandle(); - - renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStage); - resourceIds[i] = mtlTexture.GpuResourceID._impl; - } - - for (int i = 0; i < samplers.Length; i++) - { - if (samplers[i].NativePtr == IntPtr.Zero) - { - continue; - } - - var sampler = samplers[i]; - - resourceIds[i + textures.Length] = sampler.GpuResourceID._impl; - } - - var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length); - - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - - return argBuffer.Range; - } - - private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers) - { - Span resourceIds = stackalloc ulong[textures.Length + samplers.Length]; - - for (int i = 0; i < textures.Length; i++) - { - if (textures[i] == null) - { - continue; - } - - var mtlTexture = textures[i].GetHandle(); - - computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); - resourceIds[i] = mtlTexture.GpuResourceID._impl; - } - - for (int i = 0; i < samplers.Length; i++) - { - if (samplers[i].NativePtr == IntPtr.Zero) - { - continue; - } - - var sampler = samplers[i]; - - resourceIds[i + textures.Length] = sampler.GpuResourceID._impl; - } - - var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length); - - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - - return argBuffer.Range; - } } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 54ba9889d..d65aafc3e 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -36,12 +36,19 @@ namespace Ryujinx.Graphics.Metal _samplerNearest = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); _samplerLinear = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + var blitResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build(); + var blitSource = ReadMsl("Blit.metal"); _programColorBlit = new Program( [ new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], device); + ], blitResourceLayout, device); + + var colorClearResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build(); var colorClearSource = ReadMsl("ColorClear.metal"); for (int i = 0; i < Constants.MaxColorAttachments; i++) @@ -51,7 +58,7 @@ namespace Ryujinx.Graphics.Metal [ new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], device)); + ], colorClearResourceLayout, device)); } var depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); @@ -59,13 +66,18 @@ namespace Ryujinx.Graphics.Metal [ new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], device); + ], colorClearResourceLayout, device); + + var strideChangeResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); var strideChangeSource = ReadMsl("ChangeBufferStride.metal"); _programStrideChange = new Program( [ new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) - ], device, new ComputeSize(64, 1, 1)); + ], strideChangeResourceLayout, device, new ComputeSize(64, 1, 1)); } private static string ReadMsl(string fileName) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index d9ac0118c..4d744bbe4 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -12,6 +12,13 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public sealed class MetalRenderer : IRenderer { + public const int TotalSets = 4; + + public const int UniformSetIndex = 0; + public const int StorageSetIndex = 1; + public const int TextureSetIndex = 2; + public const int ImageSetIndex = 3; + private readonly MTLDevice _device; private readonly MTLCommandQueue _queue; private readonly Func _getMetalLayer; @@ -95,7 +102,7 @@ namespace Ryujinx.Graphics.Metal public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { - return new Program(shaders, _device, info.ComputeLocalSize); + return new Program(shaders, info.ResourceLayout, _device, info.ComputeLocalSize); } public ISampler CreateSampler(SamplerCreateInfo info) @@ -188,10 +195,10 @@ namespace Ryujinx.Graphics.Metal supportsViewportSwizzle: false, supportsIndirectParameters: true, supportsDepthClipControl: false, - uniformBufferSetIndex: 0, - storageBufferSetIndex: 1, - textureSetIndex: 2, - imageSetIndex: 3, + uniformBufferSetIndex: UniformSetIndex, + storageBufferSetIndex: StorageSetIndex, + textureSetIndex: TextureSetIndex, + imageSetIndex: ImageSetIndex, extraSetBaseIndex: 0, maximumExtraSets: 0, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 5635b711c..8f289392f 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -4,6 +4,8 @@ using Ryujinx.Graphics.Shader; using SharpMetal.Foundation; using SharpMetal.Metal; using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -21,7 +23,14 @@ namespace Ryujinx.Graphics.Metal private MTLComputePipelineState? _computePipelineCache; private bool _firstBackgroundUse; - public Program(ShaderSource[] shaders, MTLDevice device, ComputeSize computeLocalSize = default) + public ResourceBindingSegment[][] ClearSegments { get; } + public ResourceBindingSegment[][] BindingSegments { get; } + // Argument buffer sizes for Vertex or Compute stages + public int[] ArgumentBufferSizes { get; } + // Argument buffer sizes for Fragment stage + public int[] FragArgumentBufferSizes { get; } + + public Program(ShaderSource[] shaders, ResourceLayout resourceLayout, MTLDevice device, ComputeSize computeLocalSize = default) { ComputeLocalSize = computeLocalSize; @@ -56,9 +65,155 @@ namespace Ryujinx.Graphics.Metal } } + ClearSegments = BuildClearSegments(resourceLayout.Sets); + (BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages); + _status = ProgramLinkStatus.Success; } + private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection sets) + { + ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][]; + + for (int setIndex = 0; setIndex < sets.Count; setIndex++) + { + List currentSegments = new(); + + ResourceDescriptor currentDescriptor = default; + int currentCount = 0; + + for (int index = 0; index < sets[setIndex].Descriptors.Count; index++) + { + ResourceDescriptor descriptor = sets[setIndex].Descriptors[index]; + + if (currentDescriptor.Binding + currentCount != descriptor.Binding || + currentDescriptor.Type != descriptor.Type || + currentDescriptor.Stages != descriptor.Stages || + currentDescriptor.Count > 1 || + descriptor.Count > 1) + { + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentDescriptor.Binding, + currentCount, + currentDescriptor.Type, + currentDescriptor.Stages, + currentDescriptor.Count > 1)); + } + + currentDescriptor = descriptor; + currentCount = descriptor.Count; + } + else + { + currentCount += descriptor.Count; + } + } + + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentDescriptor.Binding, + currentCount, + currentDescriptor.Type, + currentDescriptor.Stages, + currentDescriptor.Count > 1)); + } + + segments[setIndex] = currentSegments.ToArray(); + } + + return segments; + } + + private static (ResourceBindingSegment[][], int[], int[]) BuildBindingSegments(ReadOnlyCollection setUsages) + { + ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; + int[] argBufferSizes = new int[setUsages.Count]; + int[] fragArgBufferSizes = new int[setUsages.Count]; + + for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) + { + List currentSegments = new(); + + ResourceUsage currentUsage = default; + int currentCount = 0; + + for (int index = 0; index < setUsages[setIndex].Usages.Count; index++) + { + ResourceUsage usage = setUsages[setIndex].Usages[index]; + + if (currentUsage.Binding + currentCount != usage.Binding || + currentUsage.Type != usage.Type || + currentUsage.Stages != usage.Stages || + currentUsage.ArrayLength > 1 || + usage.ArrayLength > 1) + { + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentUsage.Binding, + currentCount, + currentUsage.Type, + currentUsage.Stages, + currentUsage.ArrayLength > 1)); + + var size = currentCount * ResourcePointerSize(currentUsage.Type); + if (currentUsage.Stages.HasFlag(ResourceStages.Fragment)) + { + fragArgBufferSizes[setIndex] += size; + } + + if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) || + currentUsage.Stages.HasFlag(ResourceStages.Compute)) + { + argBufferSizes[setIndex] += size; + } + } + + currentUsage = usage; + currentCount = usage.ArrayLength; + } + else + { + currentCount++; + } + } + + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentUsage.Binding, + currentCount, + currentUsage.Type, + currentUsage.Stages, + currentUsage.ArrayLength > 1)); + + var size = currentCount * ResourcePointerSize(currentUsage.Type); + if (currentUsage.Stages.HasFlag(ResourceStages.Fragment)) + { + fragArgBufferSizes[setIndex] += size; + } + + if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) || + currentUsage.Stages.HasFlag(ResourceStages.Compute)) + { + argBufferSizes[setIndex] += size; + } + } + + segments[setIndex] = currentSegments.ToArray(); + } + + return (segments, argBufferSizes, fragArgBufferSizes); + } + + private static int ResourcePointerSize(ResourceType type) + { + return (type == ResourceType.TextureAndSampler ? 2 : 1); + } + public ProgramLinkStatus CheckProgramLink(bool blocking) { return _status; diff --git a/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs b/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs new file mode 100644 index 000000000..8e6d88c4b --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Metal +{ + readonly struct ResourceBindingSegment + { + public readonly int Binding; + public readonly int Count; + public readonly ResourceType Type; + public readonly ResourceStages Stages; + public readonly bool IsArray; + + public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, bool isArray) + { + Binding = binding; + Count = count; + Type = type; + Stages = stages; + IsArray = isArray; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs new file mode 100644 index 000000000..24ba1b6e6 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs @@ -0,0 +1,59 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + class ResourceLayoutBuilder + { + private const int TotalSets = MetalRenderer.TotalSets; + + private readonly List[] _resourceDescriptors; + private readonly List[] _resourceUsages; + + public ResourceLayoutBuilder() + { + _resourceDescriptors = new List[TotalSets]; + _resourceUsages = new List[TotalSets]; + + for (int index = 0; index < TotalSets; index++) + { + _resourceDescriptors[index] = new(); + _resourceUsages[index] = new(); + } + } + + public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding) + { + int setIndex = type switch + { + ResourceType.UniformBuffer => MetalRenderer.UniformSetIndex, + ResourceType.StorageBuffer => MetalRenderer.StorageSetIndex, + ResourceType.TextureAndSampler or ResourceType.BufferTexture => MetalRenderer.TextureSetIndex, + ResourceType.Image or ResourceType.BufferImage => MetalRenderer.ImageSetIndex, + _ => throw new ArgumentException($"Invalid resource type \"{type}\"."), + }; + + _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); + _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages)); + + return this; + } + + public ResourceLayout Build() + { + var descriptors = new ResourceDescriptorCollection[TotalSets]; + var usages = new ResourceUsageCollection[TotalSets]; + + for (int index = 0; index < TotalSets; index++) + { + descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly()); + usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly()); + } + + return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly()); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index 3c40af737..37962bbf7 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -18,133 +18,7 @@ struct ConstantBuffers { struct Textures { texture2d texture; - ulong padding_1; - ulong padding_2; - ulong padding_3; - ulong padding_4; - ulong padding_5; - ulong padding_6; - ulong padding_7; - ulong padding_8; - ulong padding_9; - ulong padding_10; - ulong padding_11; - ulong padding_12; - ulong padding_13; - ulong padding_14; - ulong padding_15; - ulong padding_16; - ulong padding_17; - ulong padding_18; - ulong padding_19; - ulong padding_20; - ulong padding_21; - ulong padding_22; - ulong padding_23; - ulong padding_24; - ulong padding_25; - ulong padding_26; - ulong padding_27; - ulong padding_28; - ulong padding_29; - ulong padding_30; - ulong padding_31; - ulong padding_32; - ulong padding_33; - ulong padding_34; - ulong padding_35; - ulong padding_36; - ulong padding_37; - ulong padding_38; - ulong padding_39; - ulong padding_40; - ulong padding_41; - ulong padding_42; - ulong padding_43; - ulong padding_44; - ulong padding_45; - ulong padding_46; - ulong padding_47; - ulong padding_48; - ulong padding_49; - ulong padding_50; - ulong padding_51; - ulong padding_52; - ulong padding_53; - ulong padding_54; - ulong padding_55; - ulong padding_56; - ulong padding_57; - ulong padding_58; - ulong padding_59; - ulong padding_60; - ulong padding_61; - ulong padding_62; - ulong padding_63; sampler sampler; - ulong padding_65; - ulong padding_66; - ulong padding_67; - ulong padding_68; - ulong padding_69; - ulong padding_70; - ulong padding_71; - ulong padding_72; - ulong padding_73; - ulong padding_74; - ulong padding_75; - ulong padding_76; - ulong padding_77; - ulong padding_78; - ulong padding_79; - ulong padding_80; - ulong padding_81; - ulong padding_82; - ulong padding_83; - ulong padding_84; - ulong padding_85; - ulong padding_86; - ulong padding_87; - ulong padding_88; - ulong padding_89; - ulong padding_90; - ulong padding_91; - ulong padding_92; - ulong padding_93; - ulong padding_94; - ulong padding_95; - ulong padding_96; - ulong padding_97; - ulong padding_98; - ulong padding_99; - ulong padding_100; - ulong padding_101; - ulong padding_102; - ulong padding_103; - ulong padding_104; - ulong padding_105; - ulong padding_106; - ulong padding_107; - ulong padding_108; - ulong padding_109; - ulong padding_110; - ulong padding_111; - ulong padding_112; - ulong padding_113; - ulong padding_114; - ulong padding_115; - ulong padding_116; - ulong padding_117; - ulong padding_118; - ulong padding_119; - ulong padding_120; - ulong padding_121; - ulong padding_122; - ulong padding_123; - ulong padding_124; - ulong padding_125; - ulong padding_126; - ulong padding_127; }; vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal index 38eedefb7..492a27d21 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal @@ -19,7 +19,6 @@ struct ConstantBuffers { }; struct StorageBuffers { - ulong padding; device InData* in_data; device OutData* out_data; }; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 5fac994b3..18cf36968 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -164,16 +164,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers, bool constant) { var name = constant ? "ConstantBuffers" : "StorageBuffers"; - var count = constant ? Defaults.MaxUniformBuffersPerStage : Defaults.MaxStorageBuffersPerStage; var addressSpace = constant ? "constant" : "device"; - var argBufferPointers = new string[count]; + List argBufferPointers = []; - foreach (BufferDefinition buffer in buffers) + // TODO: Avoid Linq if we can + var sortedBuffers = buffers.OrderBy(x => x.Binding).ToArray(); + + foreach (BufferDefinition buffer in sortedBuffers) { var needsPadding = buffer.Layout == BufferLayout.Std140; - argBufferPointers[buffer.Binding] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};"; + argBufferPointers.Add($"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};"); context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); context.EnterScope(); @@ -211,18 +213,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine($"struct {name}"); context.EnterScope(); - for (int i = 0; i < argBufferPointers.Length; i++) + foreach (var pointer in argBufferPointers) { - if (argBufferPointers[i] == null) - { - // We need to pad the struct definition in order to read - // non-contiguous resources correctly. - context.AppendLine($"ulong padding_{i};"); - } - else - { - context.AppendLine(argBufferPointers[i]); - } + context.AppendLine(pointer); } context.LeaveScope(";"); @@ -234,31 +227,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine("struct Textures"); context.EnterScope(); - var argBufferPointers = new string[Defaults.MaxTexturesPerStage * 2]; + List argBufferPointers = []; - foreach (TextureDefinition texture in textures) + // TODO: Avoid Linq if we can + var sortedTextures = textures.OrderBy(x => x.Binding).ToArray(); + + foreach (TextureDefinition texture in sortedTextures) { var textureTypeName = texture.Type.ToMslTextureType(); - argBufferPointers[texture.Binding] = $"{textureTypeName} tex_{texture.Name};"; + argBufferPointers.Add($"{textureTypeName} tex_{texture.Name};"); if (!texture.Separate && texture.Type != SamplerType.TextureBuffer) { - argBufferPointers[Defaults.MaxTexturesPerStage + texture.Binding] = $"sampler samp_{texture.Name};"; + argBufferPointers.Add($"sampler samp_{texture.Name};"); } } - for (int i = 0; i < argBufferPointers.Length; i++) + foreach (var pointer in argBufferPointers) { - if (argBufferPointers[i] == null) - { - // We need to pad the struct definition in order to read - // non-contiguous resources correctly. - context.AppendLine($"ulong padding_{i};"); - } - else - { - context.AppendLine(argBufferPointers[i]); - } + context.AppendLine(pointer); } context.LeaveScope(";"); -- 2.47.1 From d3f273cad18dc7e44f3aa78c5549472950d995ac Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 1 Jul 2024 23:07:02 +0100 Subject: [PATCH 272/368] =?UTF-8?q?Don=E2=80=99t=20use=20Enum.HasFlag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 1 - .../EncoderStateManager.cs | 28 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 448c9eaa2..6e7e6c66b 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Metal.State; using Ryujinx.Graphics.Shader; using SharpMetal.Metal; using System; -using System.Linq; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index dd0502f23..16d1b7c0b 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -177,63 +177,63 @@ namespace Ryujinx.Graphics.Metal public void RebindRenderState(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline)) + if ((_currentState.Dirty & DirtyFlags.RenderPipeline) != 0) { SetRenderPipelineState(renderCommandEncoder); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); } - if (_currentState.Dirty.HasFlag(DirtyFlags.DepthStencil)) + if ((_currentState.Dirty & DirtyFlags.DepthStencil) != 0) { SetDepthStencilState(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.DepthClamp)) + if ((_currentState.Dirty & DirtyFlags.DepthClamp) != 0) { SetDepthClamp(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.DepthBias)) + if ((_currentState.Dirty & DirtyFlags.DepthBias) != 0) { SetDepthBias(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.CullMode)) + if ((_currentState.Dirty & DirtyFlags.CullMode) != 0) { SetCullMode(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.FrontFace)) + if ((_currentState.Dirty & DirtyFlags.FrontFace) != 0) { SetFrontFace(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.StencilRef)) + if ((_currentState.Dirty & DirtyFlags.StencilRef) != 0) { SetStencilRefValue(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Viewports)) + if ((_currentState.Dirty & DirtyFlags.Viewports) != 0) { SetViewports(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Scissors)) + if ((_currentState.Dirty & DirtyFlags.Scissors) != 0) { SetScissors(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms)) + if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) { UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.UniformSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Storages)) + if ((_currentState.Dirty & DirtyFlags.Storages) != 0) { UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.StorageSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Textures)) + if ((_currentState.Dirty & DirtyFlags.Textures) != 0) { UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.TextureSetIndex); } @@ -1031,7 +1031,7 @@ namespace Ryujinx.Graphics.Metal MTLRenderStages renderStages = 0; - if (segment.Stages.HasFlag(ResourceStages.Vertex)) + if ((segment.Stages & ResourceStages.Vertex) != 0) { vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; vertResourceIdIndex++; @@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageVertex; } - if (segment.Stages.HasFlag(ResourceStages.Fragment)) + if ((segment.Stages & ResourceStages.Fragment) != 0) { fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; fragResourceIdIndex++; -- 2.47.1 From d2a4a9e9a72e5868539745334c4ce96f55ba582e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 1 Jul 2024 23:36:11 +0100 Subject: [PATCH 273/368] Least allocations in the west --- src/Ryujinx.Graphics.Metal/Constants.cs | 1 + src/Ryujinx.Graphics.Metal/EncoderState.cs | 9 ++++----- .../EncoderStateManager.cs | 16 +++++----------- src/Ryujinx.Graphics.Metal/HelperShader.cs | 12 ++++++++---- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 1ee24e308..76f4e0f87 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Metal public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; public const int MaxColorAttachments = 8; + public const int MaxViewports = 16; // TODO: Check this value public const int MaxVertexAttributes = 31; // TODO: Check this value diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 6e7e6c66b..375d9d17a 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -109,8 +109,8 @@ namespace Ryujinx.Graphics.Metal public MTLWinding Winding = MTLWinding.CounterClockwise; public bool CullBoth = false; - public MTLViewport[] Viewports = []; - public MTLScissorRect[] Scissors = []; + public MTLViewport[] Viewports = new MTLViewport[Constants.MaxViewports]; + public MTLScissorRect[] Scissors = new MTLScissorRect[Constants.MaxViewports]; // Changes to attachments take recreation! public Texture DepthStencil = default; @@ -122,9 +122,8 @@ namespace Ryujinx.Graphics.Metal public Array8 StoredBlend; public ColorF BlendColor = new(); - public VertexBufferDescriptor[] VertexBuffers = []; - public VertexAttribDescriptor[] VertexAttribs = []; - + public readonly VertexBufferDescriptor[] VertexBuffers = new VertexBufferDescriptor[Constants.MaxVertexBuffers]; + public readonly VertexAttribDescriptor[] VertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttributes]; // Dirty flags public DirtyFlags Dirty = DirtyFlags.None; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 16d1b7c0b..e24b091e7 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -510,7 +510,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) { - _currentState.VertexAttribs = vertexAttribs.ToArray(); + vertexAttribs.CopyTo(_currentState.VertexAttribs); // Update the buffers on the pipeline UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); @@ -625,11 +625,7 @@ namespace Ryujinx.Graphics.Metal // Inlineable public void UpdateScissors(ReadOnlySpan> regions) { - int maxScissors = Math.Min(regions.Length, _currentState.Viewports.Length); - - _currentState.Scissors = new MTLScissorRect[maxScissors]; - - for (int i = 0; i < maxScissors; i++) + for (int i = 0; i < regions.Length; i++) { var region = regions[i]; @@ -661,8 +657,6 @@ namespace Ryujinx.Graphics.Metal return Math.Clamp(value, 0f, 1f); } - _currentState.Viewports = new MTLViewport[viewports.Length]; - for (int i = 0; i < viewports.Length; i++) { var viewport = viewports[i]; @@ -691,7 +685,7 @@ namespace Ryujinx.Graphics.Metal public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) { - _currentState.VertexBuffers = vertexBuffers.ToArray(); + vertexBuffers.CopyTo(_currentState.VertexBuffers); // Update the buffers on the pipeline UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); @@ -1122,7 +1116,7 @@ namespace Ryujinx.Graphics.Metal MTLRenderStages renderStages = 0; - if (segment.Stages.HasFlag(ResourceStages.Vertex)) + if ((segment.Stages & ResourceStages.Vertex) != 0) { vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; vertResourceIdIndex++; @@ -1136,7 +1130,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageVertex; } - if (segment.Stages.HasFlag(ResourceStages.Fragment)) + if ((segment.Stages & ResourceStages.Fragment) != 0) { fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; fragResourceIdIndex++; diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index d65aafc3e..f2d1aabad 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -129,7 +129,7 @@ namespace Ryujinx.Graphics.Metal MathF.Abs(dstRegion.X2 - dstRegion.X1), MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - Span viewports = stackalloc Viewport[1]; + Span viewports = stackalloc Viewport[16]; viewports[0] = new Viewport( rect, @@ -145,8 +145,12 @@ namespace Ryujinx.Graphics.Metal int dstWidth = dst.Width; int dstHeight = dst.Height; + Span> scissors = stackalloc Rectangle[16]; + + scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); + _pipeline.SetRenderTargets([dst], null); - _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dstWidth, dstHeight) }); + _pipeline.SetScissors(scissors); _pipeline.SetClearLoadAction(clear); @@ -202,7 +206,7 @@ namespace Ryujinx.Graphics.Metal _renderer.BufferManager.SetData(bufferHandle, 0, region); _pipeline.SetUniformBuffers([new BufferAssignment(0, new BufferRange(bufferHandle, 0, RegionBufferSize))]); - Span viewports = stackalloc Viewport[1]; + Span viewports = stackalloc Viewport[16]; var rect = new Rectangle( MathF.Min(dstRegion.X1, dstRegion.X2), @@ -302,7 +306,7 @@ namespace Ryujinx.Graphics.Metal buffer.Holder.SetDataUnchecked(buffer.Offset, clearColor); _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - Span viewports = stackalloc Viewport[1]; + Span viewports = stackalloc Viewport[16]; // TODO: Set exact viewport! viewports[0] = new Viewport( -- 2.47.1 From 09546205b5d7c86ee296e292c0f4046e5ba78634 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 2 Jul 2024 12:00:37 +0100 Subject: [PATCH 274/368] Dirty Arg Buffers on Program Change --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 6 ++++-- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 375d9d17a..bd60e90e4 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -27,8 +27,10 @@ namespace Ryujinx.Graphics.Metal Textures = 1 << 12, Images = 1 << 13, - RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Uniforms | Storages | Textures | Images, - ComputeAll = ComputePipeline | Uniforms | Storages | Textures | Images, + ArgBuffers = Uniforms | Storages | Textures | Images, + + RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | ArgBuffers, + ComputeAll = ComputePipeline | ArgBuffers, All = RenderAll | ComputeAll, } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e24b091e7..343fcd441 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -346,13 +346,13 @@ namespace Ryujinx.Graphics.Metal { _currentState.RenderProgram = prg; - _currentState.Dirty |= DirtyFlags.RenderPipeline; + _currentState.Dirty |= DirtyFlags.RenderPipeline | DirtyFlags.ArgBuffers; } else if (prg.ComputeFunction != IntPtr.Zero) { _currentState.ComputeProgram = prg; - _currentState.Dirty |= DirtyFlags.ComputePipeline; + _currentState.Dirty |= DirtyFlags.ComputePipeline | DirtyFlags.ArgBuffers; } } -- 2.47.1 From 49a814a400a5c717cbeb05e4c9756f8516404085 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 2 Jul 2024 13:41:41 +0100 Subject: [PATCH 275/368] Fix zero buff not being reset --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 343fcd441..1c37bb3de 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -868,6 +868,7 @@ namespace Ryujinx.Graphics.Metal { ref PipelineState pipeline = ref _currentState.Pipeline; uint indexMask = 0; + int descriptorCount = 0; for (int i = 0; i < attribDescriptors.Length; i++) { @@ -923,13 +924,18 @@ namespace Ryujinx.Graphics.Metal } } + ref var zeroBufLayout = ref pipeline.Internal.VertexBindings[(int)Constants.ZeroBufferIndex]; + // Zero buffer if ((indexMask & (1u << (int)Constants.ZeroBufferIndex)) != 0) { - ref var layout = ref pipeline.Internal.VertexBindings[(int)Constants.ZeroBufferIndex]; - layout.Stride = 1; - layout.StepFunction = MTLVertexStepFunction.Constant; - layout.StepRate = 0; + zeroBufLayout.Stride = 1; + zeroBufLayout.StepFunction = MTLVertexStepFunction.Constant; + zeroBufLayout.StepRate = 0; + } + else + { + zeroBufLayout = new(); } pipeline.VertexAttributeDescriptionsCount = (uint)attribDescriptors.Length; -- 2.47.1 From 4d5b128a8137eb95ca65cc051c79a04982f804d5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 2 Jul 2024 17:49:19 +0100 Subject: [PATCH 276/368] Fix trying to reserve size 0 in staging buffer --- .../EncoderStateManager.cs | 56 +++++++++++++------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 1c37bb3de..7b1499795 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -984,8 +984,18 @@ namespace Ryujinx.Graphics.Metal return; } - var vertArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); - var fragArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.FragArgumentBufferSizes[setIndex] * sizeof(ulong)); + ScopedTemporaryBuffer vertArgBuffer = default; + ScopedTemporaryBuffer fragArgBuffer = default; + + if (program.ArgumentBufferSizes[setIndex] > 0) + { + vertArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); + } + + if (program.FragArgumentBufferSizes[setIndex] > 0) + { + fragArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.FragArgumentBufferSizes[setIndex] * sizeof(ulong)); + } Span vertResourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; Span fragResourceIds = stackalloc ulong[program.FragArgumentBufferSizes[setIndex]]; @@ -1169,14 +1179,19 @@ namespace Ryujinx.Graphics.Metal } } - vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds)); - fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds)); + if (program.ArgumentBufferSizes[setIndex] > 0) + { + vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds)); + var mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; + renderCommandEncoder.SetVertexBuffer(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + } - var mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - var mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - - renderCommandEncoder.SetVertexBuffer(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); - renderCommandEncoder.SetFragmentBuffer(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + if (program.FragArgumentBufferSizes[setIndex] > 0) + { + fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds)); + var mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; + renderCommandEncoder.SetFragmentBuffer(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + } } private void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, int setIndex) @@ -1188,7 +1203,13 @@ namespace Ryujinx.Graphics.Metal return; } - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); + ScopedTemporaryBuffer argBuffer = default; + + if (program.ArgumentBufferSizes[setIndex] > 0) + { + argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); + } + Span resourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; var resourceIdIndex = 0; @@ -1228,7 +1249,7 @@ namespace Ryujinx.Graphics.Metal mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; } - if (segment.Stages.HasFlag(ResourceStages.Compute)) + if ((segment.Stages & ResourceStages.Compute) != 0) { computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read); resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; @@ -1265,7 +1286,7 @@ namespace Ryujinx.Graphics.Metal mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; } - if (segment.Stages.HasFlag(ResourceStages.Compute)) + if ((segment.Stages & ResourceStages.Compute) != 0) { computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; @@ -1323,11 +1344,12 @@ namespace Ryujinx.Graphics.Metal } } - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - - var mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value; - - computeCommandEncoder.SetBuffer(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + if (program.ArgumentBufferSizes[setIndex] > 0) + { + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); + var mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value; + computeCommandEncoder.SetBuffer(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + } } private uint SetIndexToBindingIndex(int setIndex) -- 2.47.1 From 58527e02eea21f894fc945d580d72bbc04182a4e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 2 Jul 2024 17:52:53 +0100 Subject: [PATCH 277/368] Cleanup + Format --- src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs | 2 +- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 4 +--- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- src/Ryujinx.Graphics.Metal/Texture.cs | 6 +++--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs index 9e7dc73ea..82584629d 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs @@ -167,4 +167,4 @@ class CommandBufferEncoder CurrentEncoderType = EncoderType.Compute; return computeCommandEncoder; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 7b1499795..b198e5fd7 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -868,7 +868,6 @@ namespace Ryujinx.Graphics.Metal { ref PipelineState pipeline = ref _currentState.Pipeline; uint indexMask = 0; - int descriptorCount = 0; for (int i = 0; i < attribDescriptors.Length; i++) { @@ -1352,7 +1351,7 @@ namespace Ryujinx.Graphics.Metal } } - private uint SetIndexToBindingIndex(int setIndex) + private static uint SetIndexToBindingIndex(int setIndex) { return setIndex switch { @@ -1363,7 +1362,6 @@ namespace Ryujinx.Graphics.Metal }; } - private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) { renderCommandEncoder.SetCullMode(_currentState.CullMode); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 3e17dde41..32b520e0e 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Metal } case EncoderType.Compute: { - var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;; + var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; Encoders.ComputeEncoder.MemoryBarrier(scope); break; } diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index e938a04b8..c26890d36 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Metal descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat); _mtlTexture = _device.NewTexture(descriptor); - + MtlFormat = pixelFormat; descriptor.Dispose(); } @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Metal 0, (ulong)firstLevel, new MTLOrigin { x = 0, y = 0, z = (ulong)firstLayer }, - new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1}, + new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1 }, destinationTexture._mtlTexture, 0, (ulong)firstLevel, @@ -147,7 +147,7 @@ namespace Ryujinx.Graphics.Metal 0, (ulong)srcLevel, new MTLOrigin { x = 0, y = 0, z = (ulong)srcLayer }, - new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1}, + new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1 }, destinationTexture._mtlTexture, 0, (ulong)dstLevel, -- 2.47.1 From 82b5f8e68193333dd67345b4eb08dca6b095d047 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 3 Jul 2024 10:27:03 +0100 Subject: [PATCH 278/368] Fix a bunch of issues with texture copy and flush (#32) * Fix a bunch of issues with texture copy and flush * TextureCopy helper class, fix clear bug --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 5 +- src/Ryujinx.Graphics.Metal/SyncManager.cs | 2 +- src/Ryujinx.Graphics.Metal/Texture.cs | 255 ++++++++++++--------- src/Ryujinx.Graphics.Metal/TextureBase.cs | 3 + src/Ryujinx.Graphics.Metal/TextureCopy.cs | 201 ++++++++++++++++ 5 files changed, 351 insertions(+), 115 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/TextureCopy.cs diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index f2d1aabad..02c44365d 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -318,11 +318,14 @@ namespace Ryujinx.Graphics.Metal 0f, 1f); + Span componentMasks = stackalloc uint[index + 1]; + componentMasks[index] = componentMask; + _pipeline.SetProgram(_programsColorClear[index]); _pipeline.SetBlendState(index, new BlendDescriptor()); _pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - _pipeline.SetRenderTargetColorMasks([componentMask]); + _pipeline.SetRenderTargetColorMasks(componentMasks); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); diff --git a/src/Ryujinx.Graphics.Metal/SyncManager.cs b/src/Ryujinx.Graphics.Metal/SyncManager.cs index 1d37bacc4..ca49fe263 100644 --- a/src/Ryujinx.Graphics.Metal/SyncManager.cs +++ b/src/Ryujinx.Graphics.Metal/SyncManager.cs @@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Metal return; } - bool signaled = result.Signalled || result.Waitable.WaitForFences(false); + bool signaled = result.Signalled || result.Waitable.WaitForFences(true); if (!signaled) { diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index c26890d36..37813960e 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -57,7 +57,10 @@ namespace Ryujinx.Graphics.Metal var swizzle = GetSwizzle(info, pixelFormat); _mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + MtlFormat = pixelFormat; + FirstLayer = firstLayer; + FirstLevel = firstLevel; } private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) @@ -94,6 +97,109 @@ namespace Ryujinx.Graphics.Metal } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + CommandBufferScoped cbs = _pipeline.Cbs; + + TextureBase src = this; + TextureBase dst = (TextureBase)destination; + + var srcImage = GetHandle(); + var dstImage = dst.GetHandle(); + + if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) + { + //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + + //_gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers); + } + else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) + { + //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + + //_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers); + } + else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) + { + //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + //int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); + + //_gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels); + } + else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) + { + int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); + + // TODO: depth copy? + //_gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels); + } + else + { + TextureCopy.Copy( + cbs, + srcImage, + dstImage, + src.Info, + dst.Info, + 0,//src.FirstLayer, + 0,//dst.FirstLayer, + 0,//src.FirstLevel, + 0,//dst.FirstLevel, + 0, + firstLayer, + 0, + firstLevel); + } + } + + public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + CommandBufferScoped cbs = _pipeline.Cbs; + + TextureBase src = this; + TextureBase dst = (TextureBase)destination; + + var srcImage = GetHandle(); + var dstImage = dst.GetHandle(); + + if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) + { + //_gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); + } + else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) + { + //_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); + } + else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) + { + //_gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } + else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) + { + //_gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } + else + { + TextureCopy.Copy( + cbs, + srcImage, + dstImage, + src.Info, + dst.Info, + 0, //src.FirstLayer, + 0, //dst.FirstLayer, + 0, //src.FirstLevel, + 0, //dst.FirstLevel, + srcLayer, + dstLayer, + srcLevel, + dstLevel, + 1, + 1); + } + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { if (!_renderer.CommandBufferPool.OwnedByCurrentThread) { @@ -102,107 +208,31 @@ namespace Ryujinx.Graphics.Metal return; } - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); - - if (destination is Texture destinationTexture) - { - if (destinationTexture.Info.Target == Target.Texture3D) - { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - 0, - (ulong)firstLevel, - new MTLOrigin { x = 0, y = 0, z = (ulong)firstLayer }, - new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1 }, - destinationTexture._mtlTexture, - 0, - (ulong)firstLevel, - new MTLOrigin { x = 0, y = 0, z = (ulong)firstLayer }); - } - else - { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)firstLayer, - (ulong)firstLevel, - destinationTexture._mtlTexture, - (ulong)firstLayer, - (ulong)firstLevel, - _mtlTexture.ArrayLength, - _mtlTexture.MipmapLevelCount); - } - } - } - - public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) - { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); - - if (destination is Texture destinationTexture) - { - if (destinationTexture.Info.Target == Target.Texture3D) - { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - 0, - (ulong)srcLevel, - new MTLOrigin { x = 0, y = 0, z = (ulong)srcLayer }, - new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1 }, - destinationTexture._mtlTexture, - 0, - (ulong)dstLevel, - new MTLOrigin { x = 0, y = 0, z = (ulong)dstLayer }); - } - else - { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)srcLayer, - (ulong)srcLevel, - destinationTexture._mtlTexture, - (ulong)dstLayer, - (ulong)dstLevel, - _mtlTexture.ArrayLength, - _mtlTexture.MipmapLevelCount); - } - } - } - - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) - { var dst = (Texture)destination; bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + if (dst.Info.IsCompressed) { + Console.WriteLine("shit"); + } + _pipeline.Blit(this, destination, srcRegion, dstRegion, isDepthOrStencil, linearFilter); } public void CopyTo(BufferRange range, int layer, int level, int stride) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); var cbs = _pipeline.Cbs; int outSize = Info.GetMipSize(level); + int hostSize = GetBufferDataLength(outSize); - ulong bytesPerRow = (ulong)Info.GetMipStride(level); - ulong bytesPerImage = 0; - if (_mtlTexture.TextureType == MTLTextureType.Type3D) - { - bytesPerImage = bytesPerRow * (ulong)Info.Height; - } + int offset = range.Offset; var autoBuffer = _renderer.BufferManager.GetBuffer(range.Handle, true); var mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin(), - new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, - mtlBuffer, - (ulong)range.Offset, - bytesPerRow, - bytesPerImage); + // TODO: D32S8 conversion via temp copy holder + + CopyFromOrToBuffer(cbs, mtlBuffer, _mtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset: offset, stride: stride); } public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) @@ -217,6 +247,13 @@ namespace Ryujinx.Graphics.Metal return size; } + private void CopyDataToBuffer(Span storage, ReadOnlySpan input) + { + // TODO: D32S8 conversion + + input.CopyTo(storage); + } + private ReadOnlySpan GetDataFromBuffer(ReadOnlySpan storage, int size, Span output) { // TODO: D32S8 conversion @@ -422,37 +459,29 @@ namespace Ryujinx.Graphics.Metal buffer.Dispose(); } + private void SetData(ReadOnlySpan data, int layer, int level, int layers, int levels, bool singleSlice) + { + int bufferDataLength = GetBufferDataLength(data.Length); + + using var bufferHolder = _renderer.BufferManager.Create(bufferDataLength); + + // TODO: loadInline logic + + var cbs = _pipeline.Cbs; + + CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data); + + var buffer = bufferHolder.GetBuffer().Get(cbs).Value; + var image = GetHandle(); + + CopyFromOrToBuffer(cbs, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice); + } + public void SetData(IMemoryOwner data, int layer, int level) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true); - ulong bytesPerRow = (ulong)Info.GetMipStride(level); - ulong bytesPerImage = 0; - if (_mtlTexture.TextureType == MTLTextureType.Type3D) - { - bytesPerImage = bytesPerRow * (ulong)Info.Height; - } - - var dataSpan = data.Memory.Span; - - var buffer = _renderer.BufferManager.Create(dataSpan.Length); - buffer.SetDataUnchecked(0, dataSpan); - var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; - - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage, - new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin() - ); - - // Cleanup - buffer.Dispose(); + data.Dispose(); } public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index fcd07a66a..80d9751a8 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -21,7 +21,10 @@ namespace Ryujinx.Graphics.Metal public int Width => Info.Width; public int Height => Info.Height; public int Depth => Info.Depth; + public MTLPixelFormat MtlFormat { get; protected set; } + public int FirstLayer { get; protected set; } + public int FirstLevel { get; protected set; } public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) { diff --git a/src/Ryujinx.Graphics.Metal/TextureCopy.cs b/src/Ryujinx.Graphics.Metal/TextureCopy.cs new file mode 100644 index 000000000..ac22734df --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/TextureCopy.cs @@ -0,0 +1,201 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System; +using System.Numerics; + +namespace Ryujinx.Graphics.Metal +{ + static class TextureCopy + { + public static void Copy( + CommandBufferScoped cbs, + MTLTexture srcImage, + MTLTexture dstImage, + TextureCreateInfo srcInfo, + TextureCreateInfo dstInfo, + int srcViewLayer, + int dstViewLayer, + int srcViewLevel, + int dstViewLevel, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel) + { + int srcDepth = srcInfo.GetDepthOrLayers(); + int srcLevels = srcInfo.Levels; + + int dstDepth = dstInfo.GetDepthOrLayers(); + int dstLevels = dstInfo.Levels; + + if (dstInfo.Target == Target.Texture3D) + { + dstDepth = Math.Max(1, dstDepth >> dstLevel); + } + + int depth = Math.Min(srcDepth, dstDepth); + int levels = Math.Min(srcLevels, dstLevels); + + Copy( + cbs, + srcImage, + dstImage, + srcInfo, + dstInfo, + srcViewLayer, + dstViewLayer, + srcViewLevel, + dstViewLevel, + srcLayer, + dstLayer, + srcLevel, + dstLevel, + depth, + levels); + } + + public static void Copy( + CommandBufferScoped cbs, + MTLTexture srcImage, + MTLTexture dstImage, + TextureCreateInfo srcInfo, + TextureCreateInfo dstInfo, + int srcViewLayer, + int dstViewLayer, + int srcViewLevel, + int dstViewLevel, + int srcDepthOrLayer, + int dstDepthOrLayer, + int srcLevel, + int dstLevel, + int depthOrLayers, + int levels) + { + MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); + + int srcZ; + int srcLayer; + int srcDepth; + int srcLayers; + + if (srcInfo.Target == Target.Texture3D) + { + srcZ = srcDepthOrLayer; + srcLayer = 0; + srcDepth = depthOrLayers; + srcLayers = 1; + } + else + { + srcZ = 0; + srcLayer = srcDepthOrLayer; + srcDepth = 1; + srcLayers = depthOrLayers; + } + + int dstZ; + int dstLayer; + int dstLayers; + + if (dstInfo.Target == Target.Texture3D) + { + dstZ = dstDepthOrLayer; + dstLayer = 0; + dstLayers = 1; + } + else + { + dstZ = 0; + dstLayer = dstDepthOrLayer; + dstLayers = depthOrLayers; + } + + int srcWidth = srcInfo.Width; + int srcHeight = srcInfo.Height; + + int dstWidth = dstInfo.Width; + int dstHeight = dstInfo.Height; + + srcWidth = Math.Max(1, srcWidth >> srcLevel); + srcHeight = Math.Max(1, srcHeight >> srcLevel); + + dstWidth = Math.Max(1, dstWidth >> dstLevel); + dstHeight = Math.Max(1, dstHeight >> dstLevel); + + int blockWidth = 1; + int blockHeight = 1; + bool sizeInBlocks = false; + + // When copying from a compressed to a non-compressed format, + // the non-compressed texture will have the size of the texture + // in blocks (not in texels), so we must adjust that size to + // match the size in texels of the compressed texture. + if (!srcInfo.IsCompressed && dstInfo.IsCompressed) + { + srcWidth *= dstInfo.BlockWidth; + srcHeight *= dstInfo.BlockHeight; + blockWidth = dstInfo.BlockWidth; + blockHeight = dstInfo.BlockHeight; + + sizeInBlocks = true; + } + else if (srcInfo.IsCompressed && !dstInfo.IsCompressed) + { + dstWidth *= srcInfo.BlockWidth; + dstHeight *= srcInfo.BlockHeight; + blockWidth = srcInfo.BlockWidth; + blockHeight = srcInfo.BlockHeight; + } + + int width = Math.Min(srcWidth, dstWidth); + int height = Math.Min(srcHeight, dstHeight); + + for (int level = 0; level < levels; level++) + { + // Stop copy if we are already out of the levels range. + if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels) + { + break; + } + + int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width; + int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height; + + int layers = Math.Max(dstLayers - dstLayer, srcLayers); + + for (int layer = 0; layer < layers; layer++) + { + if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples) + { + // TODO + + Logger.Warning?.PrintMsg(LogClass.Gpu, "Unsupported mismatching sample count copy"); + } + else + { + blitCommandEncoder.CopyFromTexture( + srcImage, + (ulong)(srcViewLevel + srcLevel + level), + (ulong)(srcViewLayer + srcLayer + layer), + new MTLOrigin { z = (ulong)srcZ }, + new MTLSize { width = (ulong)copyWidth, height = (ulong)copyHeight, depth = (ulong)srcDepth }, + dstImage, + (ulong)(dstViewLevel + dstLevel + level), + (ulong)(dstViewLayer + dstLayer + layer), + new MTLOrigin { z = (ulong)dstZ }); + } + } + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (srcInfo.Target == Target.Texture3D) + { + srcDepth = Math.Max(1, srcDepth >> 1); + } + } + } + } +} -- 2.47.1 From 26da57cccde9ea02390085a41f39c31bdcdee4c5 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 3 Jul 2024 13:43:45 +0100 Subject: [PATCH 279/368] Maintain identity swizzle view of textures for rendering --- .../EncoderStateManager.cs | 4 +- src/Ryujinx.Graphics.Metal/Texture.cs | 66 +++++++++++++++++-- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index b198e5fd7..32981b0a4 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -101,10 +101,10 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < Constants.MaxColorAttachments; i++) { - if (_currentState.RenderTargets[i] != null) + if (_currentState.RenderTargets[i] is Texture tex) { var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); - passAttachment.Texture = _currentState.RenderTargets[i].GetHandle(); + tex.PopulateRenderPassAttachment(passAttachment); passAttachment.LoadAction = _currentState.ClearLoadAction ? MTLLoadAction.Clear : MTLLoadAction.Load; passAttachment.StoreAction = MTLStoreAction.Store; } diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 37813960e..e8af7e532 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -11,6 +11,9 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Texture : TextureBase, ITexture { + private MTLTexture _identitySwizzleHandle; + private bool _identityIsDifferent; + public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) { MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); @@ -34,10 +37,20 @@ namespace Ryujinx.Graphics.Metal { descriptor.ArrayLength = (ulong)Info.Depth; } + + MTLTextureSwizzleChannels swizzle = GetSwizzle(info, descriptor.PixelFormat); - descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat); + _identitySwizzleHandle = _device.NewTexture(descriptor); - _mtlTexture = _device.NewTexture(descriptor); + if (SwizzleIsIdentity(swizzle)) + { + _mtlTexture = _identitySwizzleHandle; + } + else + { + _mtlTexture = CreateDefaultView(_identitySwizzleHandle, swizzle, descriptor); + _identityIsDifferent = true; + } MtlFormat = pixelFormat; descriptor.Dispose(); @@ -56,13 +69,48 @@ namespace Ryujinx.Graphics.Metal var swizzle = GetSwizzle(info, pixelFormat); - _mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + _identitySwizzleHandle = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices); + + if (SwizzleIsIdentity(swizzle)) + { + _mtlTexture = _identitySwizzleHandle; + } + else + { + _mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + _identityIsDifferent = true; + } MtlFormat = pixelFormat; FirstLayer = firstLayer; FirstLevel = firstLevel; } + public void PopulateRenderPassAttachment(MTLRenderPassColorAttachmentDescriptor descriptor) + { + descriptor.Texture = _identitySwizzleHandle; + } + + private MTLTexture CreateDefaultView(MTLTexture texture, MTLTextureSwizzleChannels swizzle, MTLTextureDescriptor descriptor) + { + NSRange levels; + levels.location = 0; + levels.length = (ulong)Info.Levels; + NSRange slices; + slices.location = 0; + slices.length = Info.Target == Target.Texture3D ? 1 : (ulong)Info.GetDepthOrLayers(); + + return texture.NewTextureView(descriptor.PixelFormat, descriptor.TextureType, levels, slices, swizzle); + } + + private bool SwizzleIsIdentity(MTLTextureSwizzleChannels swizzle) + { + return swizzle.red == MTLTextureSwizzle.Red && + swizzle.green == MTLTextureSwizzle.Green && + swizzle.blue == MTLTextureSwizzle.Blue && + swizzle.alpha == MTLTextureSwizzle.Alpha; + } + private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) { var swizzleR = Info.SwizzleR.Convert(); @@ -237,7 +285,7 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - return new Texture(_device, _renderer, _pipeline, info, _mtlTexture, firstLayer, firstLevel); + return new Texture(_device, _renderer, _pipeline, info, _identitySwizzleHandle, firstLayer, firstLevel); } private int GetBufferDataLength(int size) @@ -521,5 +569,15 @@ namespace Ryujinx.Graphics.Metal { throw new NotImplementedException(); } + + public override void Release() + { + if (_identityIsDifferent) + { + _identitySwizzleHandle.Dispose(); + } + + base.Release(); + } } } -- 2.47.1 From 29b6e8ac53d84fb39787d75444878f0471d38a05 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 3 Jul 2024 13:46:54 +0100 Subject: [PATCH 280/368] Fix warnings --- src/Ryujinx.Graphics.Metal/BufferManager.cs | 1 + src/Ryujinx.Graphics.Metal/TextureCopy.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs index 28b6b2e24..e1a005a41 100644 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -8,6 +8,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + [SupportedOSPlatform("macos")] readonly struct ScopedTemporaryBuffer : IDisposable { private readonly BufferManager _bufferManager; diff --git a/src/Ryujinx.Graphics.Metal/TextureCopy.cs b/src/Ryujinx.Graphics.Metal/TextureCopy.cs index ac22734df..a2bec60ae 100644 --- a/src/Ryujinx.Graphics.Metal/TextureCopy.cs +++ b/src/Ryujinx.Graphics.Metal/TextureCopy.cs @@ -3,10 +3,11 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System; -using System.Numerics; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + [SupportedOSPlatform("macos")] static class TextureCopy { public static void Copy( -- 2.47.1 From d2f965885ae3f5e64791847a6c2a735faa9a9d4b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 3 Jul 2024 23:58:27 +0100 Subject: [PATCH 281/368] Fix blend state optimisation breaking attachments Fixes SM3DW --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 6 +++--- src/Ryujinx.Graphics.Metal/State/PipelineUid.cs | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 32981b0a4..42d6d0ad9 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -390,11 +390,11 @@ namespace Ryujinx.Graphics.Metal { _currentState.StoredBlend[i] = mtlBlend; - mtlBlend = new ColorBlendStateUid(); + mtlBlend.Swap(new ColorBlendStateUid()); } else if (mtlBlend.WriteMask == 0) { - mtlBlend = _currentState.StoredBlend[i]; + mtlBlend.Swap(_currentState.StoredBlend[i]); } } @@ -535,7 +535,7 @@ namespace Ryujinx.Graphics.Metal { _currentState.StoredBlend[index] = blendState; - blendState = new ColorBlendStateUid(); + blendState.Swap(new ColorBlendStateUid()); } _currentState.BlendColor = blend.BlendConstant; diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs b/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs index 4e2784b42..c986a7e23 100644 --- a/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs +++ b/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs @@ -111,6 +111,14 @@ namespace Ryujinx.Graphics.Metal readonly get => ((Id0 >> 63) & 0x1) != 0UL; set => Id0 = (Id0 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); } + + public void Swap(ColorBlendStateUid uid) + { + var format = PixelFormat; + + this = uid; + PixelFormat = format; + } } [SupportedOSPlatform("macos")] -- 2.47.1 From 8cbd44aecb571a9f70d3e63ca24390b367856ade Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 4 Jul 2024 10:19:40 +0100 Subject: [PATCH 282/368] implement compressed/uncompressed copy, fix other copies, fix int/uint output shaders (#33) --- src/Ryujinx.Graphics.Metal/Texture.cs | 8 -- src/Ryujinx.Graphics.Metal/TextureCopy.cs | 97 +++++++++++++++---- .../CodeGen/Msl/Instructions/IoMap.cs | 2 +- 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index e8af7e532..d3222dd75 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -189,10 +189,6 @@ namespace Ryujinx.Graphics.Metal dstImage, src.Info, dst.Info, - 0,//src.FirstLayer, - 0,//dst.FirstLayer, - 0,//src.FirstLevel, - 0,//dst.FirstLevel, 0, firstLayer, 0, @@ -234,10 +230,6 @@ namespace Ryujinx.Graphics.Metal dstImage, src.Info, dst.Info, - 0, //src.FirstLayer, - 0, //dst.FirstLayer, - 0, //src.FirstLevel, - 0, //dst.FirstLevel, srcLayer, dstLayer, srcLevel, diff --git a/src/Ryujinx.Graphics.Metal/TextureCopy.cs b/src/Ryujinx.Graphics.Metal/TextureCopy.cs index a2bec60ae..f869b2295 100644 --- a/src/Ryujinx.Graphics.Metal/TextureCopy.cs +++ b/src/Ryujinx.Graphics.Metal/TextureCopy.cs @@ -10,16 +10,61 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] static class TextureCopy { + public static ulong CopyFromOrToBuffer( + CommandBufferScoped cbs, + MTLBuffer buffer, + MTLTexture image, + TextureCreateInfo info, + bool to, + int dstLayer, + int dstLevel, + int x, + int y, + int width, + int height, + ulong offset = 0) + { + MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); + + bool is3D = info.Target == Target.Texture3D; + + int blockWidth = BitUtils.DivRoundUp(width, info.BlockWidth); + int blockHeight = BitUtils.DivRoundUp(height, info.BlockHeight); + ulong bytesPerRow = (ulong)BitUtils.AlignUp(blockWidth * info.BytesPerPixel, 4); + ulong bytesPerImage = bytesPerRow * (ulong)blockHeight; + + MTLOrigin origin = new MTLOrigin { x = (ulong)x, y = (ulong)y, z = is3D ? (ulong)dstLayer : 0 }; + MTLSize region = new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 }; + + uint layer = is3D ? 0 : (uint)dstLayer; + + if (to) + { + blitCommandEncoder.CopyFromTexture( + image, + layer, + (ulong)dstLevel, + origin, + region, + buffer, + offset, + bytesPerRow, + bytesPerImage); + } + else + { + blitCommandEncoder.CopyFromBuffer(buffer, offset, bytesPerRow, bytesPerImage, region, image, layer, (ulong)dstLevel, origin); + } + + return offset + bytesPerImage; + } + public static void Copy( CommandBufferScoped cbs, MTLTexture srcImage, MTLTexture dstImage, TextureCreateInfo srcInfo, TextureCreateInfo dstInfo, - int srcViewLayer, - int dstViewLayer, - int srcViewLevel, - int dstViewLevel, int srcLayer, int dstLayer, int srcLevel, @@ -45,10 +90,6 @@ namespace Ryujinx.Graphics.Metal dstImage, srcInfo, dstInfo, - srcViewLayer, - dstViewLayer, - srcViewLevel, - dstViewLevel, srcLayer, dstLayer, srcLevel, @@ -63,10 +104,6 @@ namespace Ryujinx.Graphics.Metal MTLTexture dstImage, TextureCreateInfo srcInfo, TextureCreateInfo dstInfo, - int srcViewLayer, - int dstViewLayer, - int srcViewLevel, - int dstViewLevel, int srcDepthOrLayer, int dstDepthOrLayer, int srcLevel, @@ -129,6 +166,17 @@ namespace Ryujinx.Graphics.Metal int blockHeight = 1; bool sizeInBlocks = false; + MTLBuffer tempBuffer = default; + + if (srcInfo.Format != dstInfo.Format && (srcInfo.IsCompressed || dstInfo.IsCompressed)) + { + // Compressed alias copies need to happen through a temporary buffer. + // The data is copied from the source to the buffer, then the buffer to the destination. + // The length of the buffer should be the maximum slice size for the destination. + + tempBuffer = blitCommandEncoder.Device.NewBuffer((ulong)dstInfo.GetMipSize2D(0), MTLResourceOptions.ResourceStorageModePrivate); + } + // When copying from a compressed to a non-compressed format, // the non-compressed texture will have the size of the texture // in blocks (not in texels), so we must adjust that size to @@ -168,7 +216,17 @@ namespace Ryujinx.Graphics.Metal for (int layer = 0; layer < layers; layer++) { - if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples) + if (tempBuffer.NativePtr != 0) + { + // Copy through the temp buffer + CopyFromOrToBuffer(cbs, tempBuffer, srcImage, srcInfo, true, srcLayer + layer, srcLevel + level, 0, 0, copyWidth, copyHeight); + + int dstBufferWidth = sizeInBlocks ? copyWidth * blockWidth : BitUtils.DivRoundUp(copyWidth, blockWidth); + int dstBufferHeight = sizeInBlocks ? copyHeight * blockHeight : BitUtils.DivRoundUp(copyHeight, blockHeight); + + CopyFromOrToBuffer(cbs, tempBuffer, dstImage, dstInfo, false, dstLayer + layer, dstLevel + level, 0, 0, dstBufferWidth, dstBufferHeight); + } + else if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples) { // TODO @@ -178,13 +236,13 @@ namespace Ryujinx.Graphics.Metal { blitCommandEncoder.CopyFromTexture( srcImage, - (ulong)(srcViewLevel + srcLevel + level), - (ulong)(srcViewLayer + srcLayer + layer), + (ulong)(srcLayer + layer), + (ulong)(srcLevel + level), new MTLOrigin { z = (ulong)srcZ }, new MTLSize { width = (ulong)copyWidth, height = (ulong)copyHeight, depth = (ulong)srcDepth }, dstImage, - (ulong)(dstViewLevel + dstLevel + level), - (ulong)(dstViewLayer + dstLayer + layer), + (ulong)(dstLayer + layer), + (ulong)(dstLevel + level), new MTLOrigin { z = (ulong)dstZ }); } } @@ -197,6 +255,11 @@ namespace Ryujinx.Graphics.Metal srcDepth = Math.Max(1, srcDepth >> 1); } } + + if (tempBuffer.NativePtr != 0) + { + tempBuffer.Dispose(); + } } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 5db42bbef..bd7a15e2f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.BaseVertex => ("base_vertex", AggregateType.U32), IoVariable.CtaId => ("threadgroup_position_in_grid", AggregateType.Vector3 | AggregateType.U32), IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), - IoVariable.FragmentOutputColor => ($"out.color{location}", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.FragmentOutputColor => ($"out.color{location}", definitions.GetFragmentOutputColorType(location)), IoVariable.FragmentOutputDepth => ("out.depth", AggregateType.FP32), IoVariable.FrontFacing => ("in.front_facing", AggregateType.Bool), IoVariable.GlobalId => ("thread_position_in_grid", AggregateType.Vector3 | AggregateType.U32), -- 2.47.1 From 0bb0ecb5998338a2dd2f05ea71f112f859e11876 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 4 Jul 2024 19:34:14 +0100 Subject: [PATCH 283/368] Fix invariant position not doing its job --- src/Ryujinx.Graphics.Metal/Program.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 8f289392f..290bf7b9e 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -38,8 +38,10 @@ namespace Ryujinx.Graphics.Metal { ShaderSource shader = shaders[index]; + var compileOptions = new MTLCompileOptions { PreserveInvariance = true }; + var libraryError = new NSError(IntPtr.Zero); - var shaderLibrary = device.NewLibrary(StringHelper.NSString(shader.Code), new MTLCompileOptions(IntPtr.Zero), ref libraryError); + var shaderLibrary = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, ref libraryError); if (libraryError != IntPtr.Zero) { Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code); -- 2.47.1 From 8411f698990aa71c67e14444e8a869fc2558a20b Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 5 Jul 2024 09:35:17 +0100 Subject: [PATCH 284/368] Fix preload cbs optimization (for real) (#34) * Mostly fix preload cbs. There seems to be some random flickering... * fix index buffer usage range * fix missing preflush submit before present --- src/Ryujinx.Graphics.Metal/BufferHolder.cs | 4 ++-- src/Ryujinx.Graphics.Metal/Pipeline.cs | 26 ++++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs index e0089322f..47e9cd0e3 100644 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Metal/BufferHolder.cs @@ -191,14 +191,14 @@ namespace Ryujinx.Graphics.Metal } if (cbs != null && - _pipeline.RenderPassActive && + cbs.Value.Encoders.CurrentEncoderType == EncoderType.Render && !(_buffer.HasCommandBufferDependency(cbs.Value) && _waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize))) { // If the buffer hasn't been used on the command buffer yet, try to preload the data. // This avoids ending and beginning render passes on each buffer data upload. - cbs = _pipeline.PreloadCbs; + cbs = _pipeline.GetPreloadCommandBuffer(); } if (allowCbsWait) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 32b520e0e..5e4e55ea3 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -36,7 +36,6 @@ namespace Ryujinx.Graphics.Metal internal CommandBufferScoped Cbs { get; private set; } internal CommandBufferEncoder Encoders => Cbs.Encoders; internal EncoderType CurrentEncoderType => Encoders.CurrentEncoderType; - internal bool RenderPassActive { get; private set; } public Pipeline(MTLDevice device, MetalRenderer renderer) { @@ -137,7 +136,7 @@ namespace Ryujinx.Graphics.Metal Cbs.CommandBuffer.PresentDrawable(drawable); - CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; + FlushCommandsImpl(); // TODO: Auto flush counting _renderer.SyncManager.GetAndResetWaitTicks(); @@ -146,6 +145,13 @@ namespace Ryujinx.Graphics.Metal dst.Dispose(); } + public CommandBufferScoped GetPreloadCommandBuffer() + { + PreloadCbs ??= _renderer.CommandBufferPool.Rent(); + + return PreloadCbs.Value; + } + public void FlushCommandsIfWeightExceeding(IAuto disposedResource, ulong byteWeight) { bool usedByCurrentCb = disposedResource.HasCommandBufferDependency(Cbs); @@ -383,12 +389,16 @@ namespace Ryujinx.Graphics.Metal var indexBuffer = _encoderStateManager.IndexBuffer; + ulong offset = _encoderStateManager.IndexBufferOffset; + MTLIndexType type = _encoderStateManager.IndexType; + int indexSize = type == MTLIndexType.UInt32 ? sizeof(int) : sizeof(short); + renderCommandEncoder.DrawIndexedPrimitives( primitiveType, (ulong)indexCount, - _encoderStateManager.IndexType, - indexBuffer.Get(Cbs, 0, indexCount * sizeof(int)).Value, - _encoderStateManager.IndexBufferOffset, + type, + indexBuffer.Get(Cbs, (int)offset, indexCount * indexSize).Value, + offset, (ulong)instanceCount, firstVertex, (ulong)firstInstance); @@ -533,11 +543,13 @@ namespace Ryujinx.Graphics.Metal public void SetPrimitiveRestart(bool enable, int index) { - // TODO: Supported for LineStrip and TriangleStrip + // Always active for LineStrip and TriangleStrip // https://github.com/gpuweb/gpuweb/issues/1220#issuecomment-732483263 // https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515520-drawindexedprimitives // https://stackoverflow.com/questions/70813665/how-to-render-multiple-trianglestrips-using-metal - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + + // Emulating disabling this is very difficult. It's unlikely for an index buffer to use the largest possible index, + // so it's fine nearly all of the time. } public void SetPrimitiveTopology(PrimitiveTopology topology) -- 2.47.1 From 6188872a7c2e9830fc0f2d6ecdaaa4052c694868 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 6 Jul 2024 22:33:49 +0100 Subject: [PATCH 285/368] Update binding model description comment --- .../CodeGen/Msl/Declarations.cs | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 18cf36968..d7475357c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl static class Declarations { /* - * Description of MSL Binding Strategy + * Description of MSL Binding Model * * There are a few fundamental differences between how GLSL and MSL handle I/O. * This comment will set out to describe the reasons why things are done certain ways @@ -19,24 +19,42 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl * * Main I/O Structs * - * Each stage will have a main input and output struct labeled as [Stage][In/Out], i.e VertexIn. - * Every attribute within these structs will be labeled with an [[attribute(n)]] property, - * and the overall struct will be labeled with [[stage_in]] for input structs, and defined as the + * Each stage has a main input and output struct (if applicable) labeled as [Stage][In/Out], i.e VertexIn. + * Every field within these structs is labeled with an [[attribute(n)]] property, + * and the overall struct is labeled with [[stage_in]] for input structs, and defined as the * output type of the main shader function for the output struct. This struct also contains special - * attribute-based properties like [[position]], therefore these are not confined to 'user-defined' variables. + * attribute-based properties like [[position]] that would be "built-ins" in a GLSL context. + * + * These structs are passed as inputs to all inline functions due to containing "built-ins" + * that inline functions assume access to. + * + * Vertex & Zero Buffers + * + * Binding indices 0-16 are reserved for vertex buffers, and binding 18 is reserved for the zero buffer. + * + * Uniforms & Storage Buffers + * + * Uniforms and storage buffers are tightly packed into their respective argument buffers + * (effectively ignoring binding indices at shader level), with each pointer to the corresponding + * struct that defines the layout and fields of these buffers (usually just a single data array), laid + * out one after the other in ascending order of their binding index. + * + * The uniforms argument buffer is always bound at a fixed index of 20. + * The storage buffers argument buffer is always bound at a fixed index of 21. + * + * These structs are passed as inputs to all inline functions as in GLSL or SPIRV, + * uniforms and storage buffers would be globals, and inline functions assume access to these buffers. * * Samplers & Textures * * Metal does not have a combined image sampler like sampler2D in GLSL, as a result we need to bind * an individual texture and a sampler object for each instance of a combined image sampler. - * Therefore, the binding indices of straight up textures (i.e. without a sampler) must start - * after the last sampler/texture pair (n + Number of Pairs). + * Samplers and textures are bound in a shared argument buffer. This argument buffer is tightly packed + * (effectively ignoring binding indices at shader level), with texture and their samplers (if present) + * laid out one after the other in ascending order of their binding index. * - * Uniforms + * The samplers and textures argument buffer is always bound at a fixed index of 22. * - * MSL does not have a concept of uniforms comparable to that of GLSL. As a result, instead of - * being declared outside of any function body, uniforms are part of the function signature in MSL. - * This applies to anything bound to the shader not included in the main I/O structs. */ public static void Declare(CodeGenContext context, StructuredProgramInfo info) -- 2.47.1 From 353a6ca4bb643cd93a3757b1558994691aa4cc74 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 6 Jul 2024 22:38:02 +0100 Subject: [PATCH 286/368] Formatting --- src/Ryujinx.Graphics.GAL/Format.cs | 2 +- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 2 +- src/Ryujinx.Graphics.Metal/Texture.cs | 5 +++-- src/Ryujinx.Graphics.Metal/TextureCopy.cs | 2 +- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 2 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index 0eeae8e26..b1eb68f72 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -344,7 +344,7 @@ namespace Ryujinx.Graphics.GAL /// /// Texture format /// Byte size for an element of this format (pixel, vertex attribute, etc) - public static int GetBytesPerElement(this Format format) + public static int GetBytesPerElement(this Format format) { int scalarSize = format.GetScalarSize(); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 42d6d0ad9..4c1e82269 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -839,7 +839,7 @@ namespace Ryujinx.Graphics.Metal if (_currentState.CullBoth && isTriangles) { - renderCommandEncoder.SetScissorRect(new MTLScissorRect { x = 0, y = 0, width = 0, height = 0}); + renderCommandEncoder.SetScissorRect(new MTLScissorRect { x = 0, y = 0, width = 0, height = 0 }); } else { diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index d3222dd75..a687062a5 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Metal { descriptor.ArrayLength = (ulong)Info.Depth; } - + MTLTextureSwizzleChannels swizzle = GetSwizzle(info, descriptor.PixelFormat); _identitySwizzleHandle = _device.NewTexture(descriptor); @@ -251,7 +251,8 @@ namespace Ryujinx.Graphics.Metal var dst = (Texture)destination; bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); - if (dst.Info.IsCompressed) { + if (dst.Info.IsCompressed) + { Console.WriteLine("shit"); } diff --git a/src/Ryujinx.Graphics.Metal/TextureCopy.cs b/src/Ryujinx.Graphics.Metal/TextureCopy.cs index f869b2295..b91a3cd89 100644 --- a/src/Ryujinx.Graphics.Metal/TextureCopy.cs +++ b/src/Ryujinx.Graphics.Metal/TextureCopy.cs @@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Metal else if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples) { // TODO - + Logger.Warning?.PrintMsg(LogClass.Gpu, "Unsupported mismatching sample count copy"); } else diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index a5599dbe7..b7c42aff0 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common; +using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 5af0c3539..addad83fd 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; -- 2.47.1 From b468569665d85f318d73f06d81c565a009c8b83c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 6 Jul 2024 22:56:04 +0100 Subject: [PATCH 287/368] Formatting cleanup --- .../BackgroundResources.cs | 12 +- src/Ryujinx.Graphics.Metal/BufferManager.cs | 2 +- src/Ryujinx.Graphics.Metal/CacheByRange.cs | 164 +++++++++--------- .../EncoderStateManager.cs | 66 +++---- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- .../PersistentFlushBuffer.cs | 4 +- src/Ryujinx.Graphics.Metal/StagingBuffer.cs | 4 +- src/Ryujinx.Graphics.Metal/Texture.cs | 94 +++++----- src/Ryujinx.Graphics.Metal/TextureBase.cs | 25 ++- src/Ryujinx.Graphics.Metal/TextureBuffer.cs | 20 +-- 10 files changed, 192 insertions(+), 201 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs index c06a6747b..8bf6b92bd 100644 --- a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs +++ b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs @@ -10,15 +10,13 @@ namespace Ryujinx.Graphics.Metal class BackgroundResource : IDisposable { private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; private CommandBufferPool _pool; private PersistentFlushBuffer _flushBuffer; - public BackgroundResource(MetalRenderer renderer, Pipeline pipeline) + public BackgroundResource(MetalRenderer renderer) { _renderer = renderer; - _pipeline = pipeline; } public CommandBufferPool GetPool() @@ -35,7 +33,7 @@ namespace Ryujinx.Graphics.Metal public PersistentFlushBuffer GetFlushBuffer() { - _flushBuffer ??= new PersistentFlushBuffer(_renderer, _pipeline); + _flushBuffer ??= new PersistentFlushBuffer(_renderer); return _flushBuffer; } @@ -51,14 +49,12 @@ namespace Ryujinx.Graphics.Metal class BackgroundResources : IDisposable { private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; private readonly Dictionary _resources; - public BackgroundResources(MetalRenderer renderer, Pipeline pipeline) + public BackgroundResources(MetalRenderer renderer) { _renderer = renderer; - _pipeline = pipeline; _resources = new Dictionary(); } @@ -88,7 +84,7 @@ namespace Ryujinx.Graphics.Metal { Cleanup(); - resource = new BackgroundResource(_renderer, _pipeline); + resource = new BackgroundResource(_renderer); _resources[thread] = resource; } diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs index e1a005a41..1bc7d458e 100644 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Metal _pipeline = pipeline; _buffers = new IdList(); - StagingBuffer = new StagingBuffer(_renderer, _pipeline, this); + StagingBuffer = new StagingBuffer(_renderer, this); } public BufferHandle Create(nint pointer, int size) diff --git a/src/Ryujinx.Graphics.Metal/CacheByRange.cs b/src/Ryujinx.Graphics.Metal/CacheByRange.cs index 39255de39..80a0c1018 100644 --- a/src/Ryujinx.Graphics.Metal/CacheByRange.cs +++ b/src/Ryujinx.Graphics.Metal/CacheByRange.cs @@ -13,13 +13,13 @@ namespace Ryujinx.Graphics.Metal struct I8ToI16CacheKey : ICacheKey { // Used to notify the pipeline that bindings have invalidated on dispose. - private readonly MetalRenderer _renderer; - private Auto _buffer; + // private readonly MetalRenderer _renderer; + // private Auto _buffer; public I8ToI16CacheKey(MetalRenderer renderer) { - _renderer = renderer; - _buffer = null; + // _renderer = renderer; + // _buffer = null; } public readonly bool KeyEqual(ICacheKey other) @@ -27,92 +27,92 @@ namespace Ryujinx.Graphics.Metal return other is I8ToI16CacheKey; } - public void SetBuffer(Auto buffer) + public readonly void SetBuffer(Auto buffer) { - _buffer = buffer; + // _buffer = buffer; } - public void Dispose() + public readonly void Dispose() { // TODO: Tell pipeline buffer is dirty! // _renderer.PipelineInternal.DirtyIndexBuffer(_buffer); } } - [SupportedOSPlatform("macos")] - struct AlignedVertexBufferCacheKey : ICacheKey - { - private readonly int _stride; - private readonly int _alignment; + // [SupportedOSPlatform("macos")] + // struct AlignedVertexBufferCacheKey : ICacheKey + // { + // private readonly int _stride; + // private readonly int _alignment; + // + // // Used to notify the pipeline that bindings have invalidated on dispose. + // // private readonly MetalRenderer _renderer; + // // private Auto _buffer; + // + // public AlignedVertexBufferCacheKey(MetalRenderer renderer, int stride, int alignment) + // { + // // _renderer = renderer; + // _stride = stride; + // _alignment = alignment; + // // _buffer = null; + // } + // + // public readonly bool KeyEqual(ICacheKey other) + // { + // return other is AlignedVertexBufferCacheKey entry && + // entry._stride == _stride && + // entry._alignment == _alignment; + // } + // + // public void SetBuffer(Auto buffer) + // { + // // _buffer = buffer; + // } + // + // public readonly void Dispose() + // { + // // TODO: Tell pipeline buffer is dirty! + // // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); + // } + // } - // Used to notify the pipeline that bindings have invalidated on dispose. - private readonly MetalRenderer _renderer; - private Auto _buffer; - - public AlignedVertexBufferCacheKey(MetalRenderer renderer, int stride, int alignment) - { - _renderer = renderer; - _stride = stride; - _alignment = alignment; - _buffer = null; - } - - public readonly bool KeyEqual(ICacheKey other) - { - return other is AlignedVertexBufferCacheKey entry && - entry._stride == _stride && - entry._alignment == _alignment; - } - - public void SetBuffer(Auto buffer) - { - _buffer = buffer; - } - - public readonly void Dispose() - { - // TODO: Tell pipeline buffer is dirty! - // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); - } - } - - [SupportedOSPlatform("macos")] - struct TopologyConversionCacheKey : ICacheKey - { - // TODO: Patterns - // private readonly IndexBufferPattern _pattern; - private readonly int _indexSize; - - // Used to notify the pipeline that bindings have invalidated on dispose. - private readonly MetalRenderer _renderer; - private Auto _buffer; - - public TopologyConversionCacheKey(MetalRenderer renderer, /*IndexBufferPattern pattern, */int indexSize) - { - _renderer = renderer; - // _pattern = pattern; - _indexSize = indexSize; - _buffer = null; - } - - public readonly bool KeyEqual(ICacheKey other) - { - return other is TopologyConversionCacheKey entry && - // entry._pattern == _pattern && - entry._indexSize == _indexSize; - } - - public void SetBuffer(Auto buffer) - { - _buffer = buffer; - } - - public readonly void Dispose() - { - // TODO: Tell pipeline buffer is dirty! - // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); - } - } + // [SupportedOSPlatform("macos")] + // struct TopologyConversionCacheKey : ICacheKey + // { + // // TODO: Patterns + // // private readonly IndexBufferPattern _pattern; + // private readonly int _indexSize; + // + // // Used to notify the pipeline that bindings have invalidated on dispose. + // // private readonly MetalRenderer _renderer; + // // private Auto _buffer; + // + // public TopologyConversionCacheKey(MetalRenderer renderer, /*IndexBufferPattern pattern, */int indexSize) + // { + // // _renderer = renderer; + // // _pattern = pattern; + // _indexSize = indexSize; + // // _buffer = null; + // } + // + // public readonly bool KeyEqual(ICacheKey other) + // { + // return other is TopologyConversionCacheKey entry && + // // entry._pattern == _pattern && + // entry._indexSize == _indexSize; + // } + // + // public void SetBuffer(Auto buffer) + // { + // // _buffer = buffer; + // } + // + // public readonly void Dispose() + // { + // // TODO: Tell pipeline buffer is dirty! + // // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); + // } + // } [SupportedOSPlatform("macos")] readonly struct Dependency @@ -141,8 +141,8 @@ namespace Ryujinx.Graphics.Metal { private struct Entry { - public ICacheKey Key; - public T Value; + public readonly ICacheKey Key; + public readonly T Value; public List DependencyList; public Entry(ICacheKey key, T value) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 4c1e82269..83e1835a3 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Metal } } - public void Dispose() + public readonly void Dispose() { // State @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Metal }; } - public void RestorePredrawState(PredrawState state) + public readonly void RestorePredrawState(PredrawState state) { _currentState.CullMode = state.CullMode; _currentState.DepthStencilUid = state.DepthStencilUid; @@ -89,12 +89,12 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports; } - public void SetClearLoadAction(bool clear) + public readonly void SetClearLoadAction(bool clear) { _currentState.ClearLoadAction = clear; } - public MTLRenderCommandEncoder CreateRenderCommandEncoder() + public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() { // Initialise Pass & State var renderPassDescriptor = new MTLRenderPassDescriptor(); @@ -161,7 +161,7 @@ namespace Ryujinx.Graphics.Metal return renderCommandEncoder; } - public MTLComputeCommandEncoder CreateComputeCommandEncoder() + public readonly MTLComputeCommandEncoder CreateComputeCommandEncoder() { var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); @@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty &= ~DirtyFlags.RenderAll; } - public void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) + public readonly void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) { if (_currentState.Dirty.HasFlag(DirtyFlags.ComputePipeline)) { @@ -276,7 +276,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty &= ~DirtyFlags.ComputeAll; } - private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) + private readonly void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { MTLRenderPipelineState pipelineState = _currentState.Pipeline.CreateRenderPipeline(_device, _currentState.RenderProgram); @@ -289,7 +289,7 @@ namespace Ryujinx.Graphics.Metal _currentState.BlendColor.Alpha); } - private void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) + private readonly void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) { if (_currentState.ComputeProgram == null) { @@ -301,7 +301,7 @@ namespace Ryujinx.Graphics.Metal computeCommandEncoder.SetComputePipelineState(pipelineState); } - public void UpdateIndexBuffer(BufferRange buffer, IndexType type) + public readonly void UpdateIndexBuffer(BufferRange buffer, IndexType type) { if (buffer.Handle != BufferHandle.Null) { @@ -320,12 +320,12 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdatePrimitiveTopology(PrimitiveTopology topology) + public readonly void UpdatePrimitiveTopology(PrimitiveTopology topology) { _currentState.Topology = topology; } - public void UpdateProgram(IProgram program) + public readonly void UpdateProgram(IProgram program) { Program prg = (Program)program; @@ -356,13 +356,13 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) + public readonly void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) { _currentState.FramebufferUsingColorWriteMask = false; UpdateRenderTargetsInternal(colors, depthStencil); } - public void UpdateRenderTargetColorMasks(ReadOnlySpan componentMask) + public readonly void UpdateRenderTargetColorMasks(ReadOnlySpan componentMask) { ref var blendState = ref _currentState.Pipeline.Internal.ColorBlendState; @@ -415,7 +415,7 @@ namespace Ryujinx.Graphics.Metal } } - private void UpdateRenderTargetsInternal(ITexture[] colors, ITexture depthStencil) + private readonly void UpdateRenderTargetsInternal(ITexture[] colors, ITexture depthStencil) { // TBDR GPUs don't work properly if the same attachment is bound to multiple targets, // due to each attachment being a copy of the real attachment, rather than a direct write. @@ -496,7 +496,7 @@ namespace Ryujinx.Graphics.Metal } } - private void MaskOut(ITexture[] colors, ITexture depthStencil) + private readonly void MaskOut(ITexture[] colors, ITexture depthStencil) { if (!_currentState.FramebufferUsingColorWriteMask) { @@ -508,7 +508,7 @@ namespace Ryujinx.Graphics.Metal _currentState.FramebufferUsingColorWriteMask = true; } - public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) + public readonly void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) { vertexAttribs.CopyTo(_currentState.VertexAttribs); @@ -519,7 +519,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.RenderPipeline; } - public void UpdateBlendDescriptors(int index, BlendDescriptor blend) + public readonly void UpdateBlendDescriptors(int index, BlendDescriptor blend) { ref var blendState = ref _currentState.Pipeline.Internal.ColorBlendState[index]; @@ -577,7 +577,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.DepthStencil; } - public void UpdateDepthState(DepthTestDescriptor depthTest) + public readonly void UpdateDepthState(DepthTestDescriptor depthTest) { ref DepthStencilUid uid = ref _currentState.DepthStencilUid; @@ -589,7 +589,7 @@ namespace Ryujinx.Graphics.Metal } // Inlineable - public void UpdateDepthClamp(bool clamp) + public readonly void UpdateDepthClamp(bool clamp) { _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; @@ -605,7 +605,7 @@ namespace Ryujinx.Graphics.Metal } // Inlineable - public void UpdateDepthBias(float depthBias, float slopeScale, float clamp) + public readonly void UpdateDepthBias(float depthBias, float slopeScale, float clamp) { _currentState.DepthBias = depthBias; _currentState.SlopeScale = slopeScale; @@ -683,7 +683,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.Viewports; } - public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) + public readonly void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) { vertexBuffers.CopyTo(_currentState.VertexBuffers); @@ -694,7 +694,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.RenderPipeline; } - public void UpdateUniformBuffers(ReadOnlySpan buffers) + public readonly void UpdateUniformBuffers(ReadOnlySpan buffers) { foreach (BufferAssignment assignment in buffers) { @@ -711,7 +711,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.Uniforms; } - public void UpdateStorageBuffers(ReadOnlySpan buffers) + public readonly void UpdateStorageBuffers(ReadOnlySpan buffers) { foreach (BufferAssignment assignment in buffers) { @@ -728,7 +728,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.Storages; } - public void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) + public readonly void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) { for (int i = 0; i < buffers.Length; i++) { @@ -767,7 +767,7 @@ namespace Ryujinx.Graphics.Metal } // Inlineable - public void UpdateFrontFace(FrontFace frontFace) + public readonly void UpdateFrontFace(FrontFace frontFace) { _currentState.Winding = frontFace.Convert(); @@ -782,7 +782,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.FrontFace; } - private void UpdateStencilRefValue(int frontRef, int backRef) + private readonly void UpdateStencilRefValue(int frontRef, int backRef) { _currentState.FrontRefValue = frontRef; _currentState.BackRefValue = backRef; @@ -797,9 +797,9 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.StencilRef; } - public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) + public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) { - if (texture is TextureBuffer textureBuffer) + if (texture is TextureBuffer) { // TODO: Texture buffers } @@ -853,7 +853,7 @@ namespace Ryujinx.Graphics.Metal } } - private unsafe void SetViewports(MTLRenderCommandEncoder renderCommandEncoder) + private readonly unsafe void SetViewports(MTLRenderCommandEncoder renderCommandEncoder) { if (_currentState.Viewports.Length > 0) { @@ -864,7 +864,7 @@ namespace Ryujinx.Graphics.Metal } } - private void UpdatePipelineVertexState(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) + private readonly void UpdatePipelineVertexState(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) { ref PipelineState pipeline = ref _currentState.Pipeline; uint indexMask = 0; @@ -941,7 +941,7 @@ namespace Ryujinx.Graphics.Metal pipeline.VertexBindingDescriptionsCount = Constants.ZeroBufferIndex + 1; // TODO: move this out? } - private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) + private readonly void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) { for (int i = 0; i < bufferDescriptors.Length; i++) { @@ -974,7 +974,7 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex); } - private void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, int setIndex) + private readonly void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, int setIndex) { var bindingSegments = program.BindingSegments[setIndex]; @@ -1193,7 +1193,7 @@ namespace Ryujinx.Graphics.Metal } } - private void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, int setIndex) + private readonly void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, int setIndex) { var bindingSegments = program.BindingSegments[setIndex]; diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 4d744bbe4..fa24e70a0 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.InitEncoderStateManager(BufferManager); - BackgroundResources = new BackgroundResources(this, _pipeline); + BackgroundResources = new BackgroundResources(this); HelperShader = new HelperShader(_device, this, _pipeline); SyncManager = new SyncManager(this); } diff --git a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs b/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs index a1834f0b7..fa3df47db 100644 --- a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs @@ -8,14 +8,12 @@ namespace Ryujinx.Graphics.Metal internal class PersistentFlushBuffer : IDisposable { private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; private BufferHolder _flushStorage; - public PersistentFlushBuffer(MetalRenderer renderer, Pipeline pipeline) + public PersistentFlushBuffer(MetalRenderer renderer) { _renderer = renderer; - _pipeline = pipeline; } private BufferHolder ResizeIfNeeded(int size) diff --git a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs index d739cdd3f..b250b87f2 100644 --- a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs @@ -31,7 +31,6 @@ namespace Ryujinx.Graphics.Metal private int _freeSize; private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; private readonly BufferHolder _buffer; private readonly int _resourceAlignment; @@ -52,10 +51,9 @@ namespace Ryujinx.Graphics.Metal private readonly Queue _pendingCopies; - public StagingBuffer(MetalRenderer renderer, Pipeline pipeline, BufferManager bufferManager) + public StagingBuffer(MetalRenderer renderer, BufferManager bufferManager) { _renderer = renderer; - _pipeline = pipeline; Handle = bufferManager.CreateWithHandle(BufferSize, out _buffer); _pendingCopies = new Queue(); diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index a687062a5..fa5def60b 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Metal class Texture : TextureBase, ITexture { private MTLTexture _identitySwizzleHandle; - private bool _identityIsDifferent; + private readonly bool _identityIsDifferent; public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) { @@ -40,15 +40,15 @@ namespace Ryujinx.Graphics.Metal MTLTextureSwizzleChannels swizzle = GetSwizzle(info, descriptor.PixelFormat); - _identitySwizzleHandle = _device.NewTexture(descriptor); + _identitySwizzleHandle = Device.NewTexture(descriptor); if (SwizzleIsIdentity(swizzle)) { - _mtlTexture = _identitySwizzleHandle; + MtlTexture = _identitySwizzleHandle; } else { - _mtlTexture = CreateDefaultView(_identitySwizzleHandle, swizzle, descriptor); + MtlTexture = CreateDefaultView(_identitySwizzleHandle, swizzle, descriptor); _identityIsDifferent = true; } @@ -73,11 +73,11 @@ namespace Ryujinx.Graphics.Metal if (SwizzleIsIdentity(swizzle)) { - _mtlTexture = _identitySwizzleHandle; + MtlTexture = _identitySwizzleHandle; } else { - _mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + MtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); _identityIsDifferent = true; } @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { - CommandBufferScoped cbs = _pipeline.Cbs; + CommandBufferScoped cbs = Pipeline.Cbs; TextureBase src = this; TextureBase dst = (TextureBase)destination; @@ -156,30 +156,30 @@ namespace Ryujinx.Graphics.Metal if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) { - //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - //_gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers); + // _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers); } else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) { - //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - //_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers); + // _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers); } else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) { - //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - //int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); + // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + // int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); - //_gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels); + // _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels); } else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) { - int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); + // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + // int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); // TODO: depth copy? - //_gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels); + // _gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels); } else { @@ -198,7 +198,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { - CommandBufferScoped cbs = _pipeline.Cbs; + CommandBufferScoped cbs = Pipeline.Cbs; TextureBase src = this; TextureBase dst = (TextureBase)destination; @@ -208,19 +208,19 @@ namespace Ryujinx.Graphics.Metal if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) { - //_gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); + // _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); } else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) { - //_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); + // _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); } else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) { - //_gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + // _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); } else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) { - //_gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + // _gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); } else { @@ -241,7 +241,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { - if (!_renderer.CommandBufferPool.OwnedByCurrentThread) + if (!Renderer.CommandBufferPool.OwnedByCurrentThread) { Logger.Warning?.PrintMsg(LogClass.Gpu, "Metal doesn't currently support scaled blit on background thread."); @@ -256,29 +256,29 @@ namespace Ryujinx.Graphics.Metal Console.WriteLine("shit"); } - _pipeline.Blit(this, destination, srcRegion, dstRegion, isDepthOrStencil, linearFilter); + Pipeline.Blit(this, destination, srcRegion, dstRegion, isDepthOrStencil, linearFilter); } public void CopyTo(BufferRange range, int layer, int level, int stride) { - var cbs = _pipeline.Cbs; + var cbs = Pipeline.Cbs; int outSize = Info.GetMipSize(level); int hostSize = GetBufferDataLength(outSize); int offset = range.Offset; - var autoBuffer = _renderer.BufferManager.GetBuffer(range.Handle, true); + var autoBuffer = Renderer.BufferManager.GetBuffer(range.Handle, true); var mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; // TODO: D32S8 conversion via temp copy holder - CopyFromOrToBuffer(cbs, mtlBuffer, _mtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset: offset, stride: stride); + CopyFromOrToBuffer(cbs, mtlBuffer, MtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset: offset, stride: stride); } public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - return new Texture(_device, _renderer, _pipeline, info, _identitySwizzleHandle, firstLayer, firstLevel); + return new Texture(Device, Renderer, Pipeline, info, _identitySwizzleHandle, firstLayer, firstLevel); } private int GetBufferDataLength(int size) @@ -415,13 +415,13 @@ namespace Ryujinx.Graphics.Metal public PinnedSpan GetData() { - BackgroundResource resources = _renderer.BackgroundResources.Get(); + BackgroundResource resources = Renderer.BackgroundResources.Get(); - if (_renderer.CommandBufferPool.OwnedByCurrentThread) + if (Renderer.CommandBufferPool.OwnedByCurrentThread) { - _renderer.FlushAllCommands(); + Renderer.FlushAllCommands(); - return PinnedSpan.UnsafeFromSpan(GetData(_renderer.CommandBufferPool, resources.GetFlushBuffer())); + return PinnedSpan.UnsafeFromSpan(GetData(Renderer.CommandBufferPool, resources.GetFlushBuffer())); } return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer())); @@ -429,13 +429,13 @@ namespace Ryujinx.Graphics.Metal public PinnedSpan GetData(int layer, int level) { - BackgroundResource resources = _renderer.BackgroundResources.Get(); + BackgroundResource resources = Renderer.BackgroundResources.Get(); - if (_renderer.CommandBufferPool.OwnedByCurrentThread) + if (Renderer.CommandBufferPool.OwnedByCurrentThread) { - _renderer.FlushAllCommands(); + Renderer.FlushAllCommands(); - return PinnedSpan.UnsafeFromSpan(GetData(_renderer.CommandBufferPool, resources.GetFlushBuffer(), layer, level)); + return PinnedSpan.UnsafeFromSpan(GetData(Renderer.CommandBufferPool, resources.GetFlushBuffer(), layer, level)); } return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level)); @@ -443,13 +443,13 @@ namespace Ryujinx.Graphics.Metal public void SetData(IMemoryOwner data) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + var blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder(); var dataSpan = data.Memory.Span; - var buffer = _renderer.BufferManager.Create(dataSpan.Length); + var buffer = Renderer.BufferManager.Create(dataSpan.Length); buffer.SetDataUnchecked(0, dataSpan); - var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; + var mtlBuffer = buffer.GetBuffer(false).Get(Pipeline.Cbs).Value; int width = Info.Width; int height = Info.Height; @@ -478,7 +478,7 @@ namespace Ryujinx.Graphics.Metal (ulong)Info.GetMipStride(level), (ulong)mipSize, new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, - _mtlTexture, + MtlTexture, (ulong)layer, (ulong)level, new MTLOrigin() @@ -504,11 +504,11 @@ namespace Ryujinx.Graphics.Metal { int bufferDataLength = GetBufferDataLength(data.Length); - using var bufferHolder = _renderer.BufferManager.Create(bufferDataLength); + using var bufferHolder = Renderer.BufferManager.Create(bufferDataLength); // TODO: loadInline logic - var cbs = _pipeline.Cbs; + var cbs = Pipeline.Cbs; CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data); @@ -527,20 +527,20 @@ namespace Ryujinx.Graphics.Metal public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + var blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder(); ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; - if (_mtlTexture.TextureType == MTLTextureType.Type3D) + if (MtlTexture.TextureType == MTLTextureType.Type3D) { bytesPerImage = bytesPerRow * (ulong)Info.Height; } var dataSpan = data.Memory.Span; - var buffer = _renderer.BufferManager.Create(dataSpan.Length); + var buffer = Renderer.BufferManager.Create(dataSpan.Length); buffer.SetDataUnchecked(0, dataSpan); - var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; + var mtlBuffer = buffer.GetBuffer(false).Get(Pipeline.Cbs).Value; blitCommandEncoder.CopyFromBuffer( mtlBuffer, @@ -548,7 +548,7 @@ namespace Ryujinx.Graphics.Metal bytesPerRow, bytesPerImage, new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, - _mtlTexture, + MtlTexture, (ulong)layer, (ulong)level, new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index 80d9751a8..964f06e22 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -10,14 +10,13 @@ namespace Ryujinx.Graphics.Metal { private bool _disposed; - protected readonly TextureCreateInfo _info; - protected readonly Pipeline _pipeline; - protected readonly MTLDevice _device; - protected readonly MetalRenderer _renderer; + protected readonly Pipeline Pipeline; + protected readonly MTLDevice Device; + protected readonly MetalRenderer Renderer; - protected MTLTexture _mtlTexture; + protected MTLTexture MtlTexture; - public TextureCreateInfo Info => _info; + public readonly TextureCreateInfo Info; public int Width => Info.Width; public int Height => Info.Height; public int Depth => Info.Depth; @@ -28,10 +27,10 @@ namespace Ryujinx.Graphics.Metal public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) { - _device = device; - _renderer = renderer; - _pipeline = pipeline; - _info = info; + Device = device; + Renderer = renderer; + Pipeline = pipeline; + Info = info; } public MTLTexture GetHandle() @@ -41,7 +40,7 @@ namespace Ryujinx.Graphics.Metal return new MTLTexture(IntPtr.Zero); } - return _mtlTexture; + return MtlTexture; } public virtual void Release() @@ -51,9 +50,9 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { - if (_mtlTexture != IntPtr.Zero) + if (MtlTexture != IntPtr.Zero) { - _mtlTexture.Dispose(); + MtlTexture.Dispose(); } _disposed = true; } diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs index 033e12105..388b77e23 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs @@ -37,23 +37,23 @@ namespace Ryujinx.Graphics.Metal // Find the parent buffer, and try to build a texture from it. // TODO: texture uses should register read/write usage on the assigned buffer. - Auto bufferAuto = _renderer.BufferManager.GetBuffer(_bufferHandle, false); + Auto bufferAuto = Renderer.BufferManager.GetBuffer(_bufferHandle, false); - if (_mtlTexture.NativePtr != 0) + if (MtlTexture.NativePtr != 0) { - _mtlTexture.Dispose(); + MtlTexture.Dispose(); } if (bufferAuto == null) { - _mtlTexture = default; + MtlTexture = default; } else { - DisposableBuffer buffer = bufferAuto.Get(_pipeline.Cbs, _offset, _size); + DisposableBuffer buffer = bufferAuto.Get(Pipeline.Cbs, _offset, _size); _descriptor.Width = (uint)(_size / Info.BytesPerPixel); - _mtlTexture = buffer.Value.NewTexture(_descriptor, (ulong)_offset, (ulong)_size); + MtlTexture = buffer.Value.NewTexture(_descriptor, (ulong)_offset, (ulong)_size); } } @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Metal public PinnedSpan GetData() { - return _renderer.GetBufferData(_bufferHandle, _offset, _size); + return Renderer.GetBufferData(_bufferHandle, _offset, _size); } public PinnedSpan GetData(int layer, int level) @@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(IMemoryOwner data) { - _renderer.SetBufferData(_bufferHandle, _offset, data.Memory.Span); + Renderer.SetBufferData(_bufferHandle, _offset, data.Memory.Span); data.Dispose(); } @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Metal if (_bufferHandle == buffer.Handle && _offset == buffer.Offset && _size == buffer.Size && - _bufferCount == _renderer.BufferManager.BufferCount) + _bufferCount == Renderer.BufferManager.BufferCount) { return; } @@ -121,7 +121,7 @@ namespace Ryujinx.Graphics.Metal _bufferHandle = buffer.Handle; _offset = buffer.Offset; _size = buffer.Size; - _bufferCount = _renderer.BufferManager.BufferCount; + _bufferCount = Renderer.BufferManager.BufferCount; RebuildStorage(); } -- 2.47.1 From fcd2adecc5388d25b6dc7f98c6b30ac7e1a8c30e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 8 Jul 2024 12:02:42 +0100 Subject: [PATCH 288/368] Better index buffer management --- src/Ryujinx.Graphics.Metal/BufferManager.cs | 12 +++ src/Ryujinx.Graphics.Metal/EncoderState.cs | 6 +- .../EncoderStateManager.cs | 21 ++---- .../IndexBufferState.cs | 74 +++++++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 29 ++++---- 5 files changed, 108 insertions(+), 34 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/IndexBufferState.cs diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs index 1bc7d458e..71620f424 100644 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -135,6 +135,18 @@ namespace Ryujinx.Graphics.Metal return null; } + public Auto GetBuffer(BufferHandle handle, bool isWrite, out int size) + { + if (TryGetBuffer(handle, out var holder)) + { + size = holder.Size; + return holder.GetBuffer(isWrite); + } + + size = 0; + return null; + } + public Auto GetBuffer(BufferHandle handle, int offset, int size, bool isWrite) { if (TryGetBuffer(handle, out var holder)) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index bd60e90e4..5c82fb9cf 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -93,9 +93,7 @@ namespace Ryujinx.Graphics.Metal public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings]; - public Auto IndexBuffer = default; - public MTLIndexType IndexType = MTLIndexType.UInt16; - public ulong IndexBufferOffset = 0; + public IndexBufferState IndexBuffer = default; public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip; @@ -115,7 +113,7 @@ namespace Ryujinx.Graphics.Metal public MTLScissorRect[] Scissors = new MTLScissorRect[Constants.MaxViewports]; // Changes to attachments take recreation! - public Texture DepthStencil = default; + public Texture DepthStencil; public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments]; public ITexture PreMaskDepthStencil = default; public ITexture[] PreMaskRenderTargets; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 83e1835a3..b29a472ce 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -23,9 +23,7 @@ namespace Ryujinx.Graphics.Metal private readonly EncoderState _mainState = new(); private EncoderState _currentState; - public readonly Auto IndexBuffer => _currentState.IndexBuffer; - public readonly MTLIndexType IndexType => _currentState.IndexType; - public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset; + public readonly IndexBufferState IndexBuffer => _currentState.IndexBuffer; public readonly PrimitiveTopology Topology => _currentState.Topology; public readonly Texture[] RenderTargets => _currentState.RenderTargets; public readonly Texture DepthStencil => _currentState.DepthStencil; @@ -305,18 +303,11 @@ namespace Ryujinx.Graphics.Metal { if (buffer.Handle != BufferHandle.Null) { - if (type == GAL.IndexType.UByte) - { - _currentState.IndexType = MTLIndexType.UInt16; - _currentState.IndexBufferOffset = 0; - _currentState.IndexBuffer = _bufferManager.GetBufferI8ToI16(_pipeline.Cbs, buffer.Handle, buffer.Offset, buffer.Size); - } - else - { - _currentState.IndexType = type.Convert(); - _currentState.IndexBufferOffset = (ulong)buffer.Offset; - _currentState.IndexBuffer = _bufferManager.GetBuffer(buffer.Handle, false); - } + _currentState.IndexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type); + } + else + { + _currentState.IndexBuffer = IndexBufferState.Null; } } diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs b/src/Ryujinx.Graphics.Metal/IndexBufferState.cs new file mode 100644 index 000000000..9eaaf9a19 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/IndexBufferState.cs @@ -0,0 +1,74 @@ +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal struct IndexBufferState + { + public static IndexBufferState Null => new(BufferHandle.Null, 0, 0); + + private readonly int _offset; + private readonly int _size; + private readonly IndexType _type; + + private readonly BufferHandle _handle; + + public IndexBufferState(BufferHandle handle, int offset, int size, IndexType type) + { + _handle = handle; + _offset = offset; + _size = size; + _type = type; + } + + public IndexBufferState(BufferHandle handle, int offset, int size) + { + _handle = handle; + _offset = offset; + _size = size; + _type = IndexType.UInt; + } + + public (MTLBuffer, int, MTLIndexType) GetIndexBuffer(MetalRenderer renderer, CommandBufferScoped cbs) + { + Auto autoBuffer; + int offset, size; + MTLIndexType type; + + if (_type == IndexType.UByte) + { + // Index type is not supported. Convert to I16. + autoBuffer = renderer.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size); + + type = MTLIndexType.UInt16; + offset = 0; + size = _size * 2; + } + else + { + autoBuffer = renderer.BufferManager.GetBuffer(_handle, false, out int bufferSize); + + if (_offset >= bufferSize) + { + autoBuffer = null; + } + + type = _type.Convert(); + offset = _offset; + size = _size; + } + + if (autoBuffer != null) + { + DisposableBuffer buffer = autoBuffer.Get(cbs, offset, size); + + return (buffer.Value, offset, type); + } + + return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt16); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 5e4e55ea3..d5c04884a 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -377,8 +377,6 @@ namespace Ryujinx.Graphics.Metal return; } - var renderCommandEncoder = GetOrCreateRenderEncoder(true); - // TODO: Reindex unsupported topologies if (TopologyUnsupported(_encoderStateManager.Topology)) { @@ -387,21 +385,22 @@ namespace Ryujinx.Graphics.Metal var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - var indexBuffer = _encoderStateManager.IndexBuffer; + (MTLBuffer mtlBuffer, int offset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); - ulong offset = _encoderStateManager.IndexBufferOffset; - MTLIndexType type = _encoderStateManager.IndexType; - int indexSize = type == MTLIndexType.UInt32 ? sizeof(int) : sizeof(short); + if (mtlBuffer.NativePtr != IntPtr.Zero) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(true); - renderCommandEncoder.DrawIndexedPrimitives( - primitiveType, - (ulong)indexCount, - type, - indexBuffer.Get(Cbs, (int)offset, indexCount * indexSize).Value, - offset, - (ulong)instanceCount, - firstVertex, - (ulong)firstInstance); + renderCommandEncoder.DrawIndexedPrimitives( + primitiveType, + (ulong)indexCount, + type, + mtlBuffer, + (ulong)offset, + (ulong)instanceCount, + firstVertex, + (ulong)firstInstance); + } } public void DrawIndexedIndirect(BufferRange indirectBuffer) -- 2.47.1 From c399868ddff438c6c757c1e9601b4930e42919bd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 8 Jul 2024 13:18:48 +0100 Subject: [PATCH 289/368] Fix LOD --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 93eaee5dd..fce76012e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -155,7 +155,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return GenerateLoadOrStore(context, operation, isStore: false); } - // TODO: check this public static string Lod(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; @@ -183,7 +182,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions coordsExpr = GetSourceExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32); } - return $"textures.tex_{samplerName}.calculate_unclamped_lod(textures.samp_{samplerName}, {coordsExpr}){GetMaskMultiDest(texOp.Index)}"; + var clamped = $"textures.tex_{samplerName}.calculate_clamped_lod(textures.samp_{samplerName}, {coordsExpr})"; + var unclamped = $"textures.tex_{samplerName}.calculate_unclamped_lod(textures.samp_{samplerName}, {coordsExpr})"; + + return $"float2({clamped}, {unclamped}){GetMask(texOp.Index)}"; } public static string Store(CodeGenContext context, AstOperation operation) -- 2.47.1 From 1f133040bd5fddfcb55436d3b3332762118a8776 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 8 Jul 2024 13:55:46 +0100 Subject: [PATCH 290/368] Better vertex buffer management --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 2 +- .../EncoderStateManager.cs | 41 +++++++------ .../VertexBufferState.cs | 60 +++++++++++++++++++ 3 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/VertexBufferState.cs diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 5c82fb9cf..d7b9bb8c8 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Metal public Array8 StoredBlend; public ColorF BlendColor = new(); - public readonly VertexBufferDescriptor[] VertexBuffers = new VertexBufferDescriptor[Constants.MaxVertexBuffers]; + public readonly VertexBufferState[] VertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers]; public readonly VertexAttribDescriptor[] VertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttributes]; // Dirty flags public DirtyFlags Dirty = DirtyFlags.None; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index b29a472ce..e3ace6e9a 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -52,8 +52,6 @@ namespace Ryujinx.Graphics.Metal public readonly void Dispose() { - // State - _depthStencilCache.Dispose(); } @@ -676,7 +674,24 @@ namespace Ryujinx.Graphics.Metal public readonly void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) { - vertexBuffers.CopyTo(_currentState.VertexBuffers); + for (int i = 0; i < Constants.MaxVertexBuffers; i++) + { + if (i < vertexBuffers.Length) + { + var vertexBuffer = vertexBuffers[i]; + + _currentState.VertexBuffers[i] = new VertexBufferState( + vertexBuffer.Buffer.Handle, + vertexBuffer.Buffer.Offset, + vertexBuffer.Buffer.Size, + vertexBuffer.Divisor, + vertexBuffer.Stride); + } + else + { + _currentState.VertexBuffers[i] = VertexBufferState.Null; + } + } // Update the buffers on the pipeline UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); @@ -855,7 +870,7 @@ namespace Ryujinx.Graphics.Metal } } - private readonly void UpdatePipelineVertexState(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) + private readonly void UpdatePipelineVertexState(VertexBufferState[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) { ref PipelineState pipeline = ref _currentState.Pipeline; uint indexMask = 0; @@ -932,24 +947,16 @@ namespace Ryujinx.Graphics.Metal pipeline.VertexBindingDescriptionsCount = Constants.ZeroBufferIndex + 1; // TODO: move this out? } - private readonly void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) + private readonly void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferState[] bufferStates) { - for (int i = 0; i < bufferDescriptors.Length; i++) + for (int i = 0; i < bufferStates.Length; i++) { - Auto autoBuffer = bufferDescriptors[i].Buffer.Handle == BufferHandle.Null - ? null - : _bufferManager.GetBuffer(bufferDescriptors[i].Buffer.Handle, bufferDescriptors[i].Buffer.Write); + (MTLBuffer mtlBuffer, int offset) = bufferStates[i].GetVertexBuffer(_bufferManager, _pipeline.Cbs); - var range = bufferDescriptors[i].Buffer; - var offset = range.Offset; - - if (autoBuffer == null) + if (mtlBuffer.NativePtr != IntPtr.Zero) { - continue; + renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)i); } - - var mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Size, range.Write).Value; - renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)i); } Auto autoZeroBuffer = _zeroBuffer == BufferHandle.Null diff --git a/src/Ryujinx.Graphics.Metal/VertexBufferState.cs b/src/Ryujinx.Graphics.Metal/VertexBufferState.cs new file mode 100644 index 000000000..277366b89 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/VertexBufferState.cs @@ -0,0 +1,60 @@ +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal struct VertexBufferState + { + public static VertexBufferState Null => new(BufferHandle.Null, 0, 0, 0); + + private readonly BufferHandle _handle; + private readonly int _offset; + private readonly int _size; + + public readonly int Stride; + public readonly int Divisor; + + public VertexBufferState(BufferHandle handle, int offset, int size, int divisor, int stride = 0) + { + _handle = handle; + _offset = offset; + _size = size; + + Stride = stride; + Divisor = divisor; + } + + public (MTLBuffer, int) GetVertexBuffer(BufferManager bufferManager, CommandBufferScoped cbs) + { + Auto autoBuffer = null; + + if (_handle != BufferHandle.Null) + { + // TODO: Handle restride if necessary + + autoBuffer = bufferManager.GetBuffer(_handle, false, out int size); + + // The original stride must be reapplied in case it was rewritten. + // TODO: Handle restride if necessary + + if (_offset >= size) + { + autoBuffer = null; + } + } + + if (autoBuffer != null) + { + int offset = _offset; + var buffer = autoBuffer.Get(cbs, offset, _size).Value; + + return (buffer, offset); + } + + return (new MTLBuffer(IntPtr.Zero), 0); + } + } +} -- 2.47.1 From e0cd935c2841c8e34e89c0daaaa7bdcfab45ead1 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 19 Jul 2024 19:39:58 +0100 Subject: [PATCH 291/368] GAL ResourceUsage Changes TODO: Guest Barrier Defer --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 2 +- src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 02c44365d..647e4ba8b 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Metal var strideChangeResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); var strideChangeSource = ReadMsl("ChangeBufferStride.metal"); _programStrideChange = new Program( diff --git a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs index 24ba1b6e6..e969ce82b 100644 --- a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs +++ b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Metal } } - public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding) + public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false) { int setIndex = type switch { @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Metal }; _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); - _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages)); + _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write)); return this; } -- 2.47.1 From 5e72eb8362fd4f11439ebafbbb4a9417b718b5b1 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 19 Jul 2024 19:45:09 +0100 Subject: [PATCH 292/368] Make dotnet format happy --- src/Ryujinx.Graphics.Metal/IndexBufferState.cs | 12 ++---------- src/Ryujinx.Graphics.Metal/VertexBufferState.cs | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs b/src/Ryujinx.Graphics.Metal/IndexBufferState.cs index 9eaaf9a19..7cd2ff42e 100644 --- a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs +++ b/src/Ryujinx.Graphics.Metal/IndexBufferState.cs @@ -6,7 +6,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - internal struct IndexBufferState + readonly internal struct IndexBufferState { public static IndexBufferState Null => new(BufferHandle.Null, 0, 0); @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Metal private readonly BufferHandle _handle; - public IndexBufferState(BufferHandle handle, int offset, int size, IndexType type) + public IndexBufferState(BufferHandle handle, int offset, int size, IndexType type = IndexType.UInt) { _handle = handle; _offset = offset; @@ -24,14 +24,6 @@ namespace Ryujinx.Graphics.Metal _type = type; } - public IndexBufferState(BufferHandle handle, int offset, int size) - { - _handle = handle; - _offset = offset; - _size = size; - _type = IndexType.UInt; - } - public (MTLBuffer, int, MTLIndexType) GetIndexBuffer(MetalRenderer renderer, CommandBufferScoped cbs) { Auto autoBuffer; diff --git a/src/Ryujinx.Graphics.Metal/VertexBufferState.cs b/src/Ryujinx.Graphics.Metal/VertexBufferState.cs index 277366b89..6591fe6d6 100644 --- a/src/Ryujinx.Graphics.Metal/VertexBufferState.cs +++ b/src/Ryujinx.Graphics.Metal/VertexBufferState.cs @@ -6,7 +6,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - internal struct VertexBufferState + readonly internal struct VertexBufferState { public static VertexBufferState Null => new(BufferHandle.Null, 0, 0, 0); -- 2.47.1 From d9b322688cdbe3e0c056bb76cef000915cc7694f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 22 Jul 2024 13:40:10 +0100 Subject: [PATCH 293/368] Shader cache support --- Directory.Packages.props | 2 +- .../Shader/DiskCache/DiskCacheHostStorage.cs | 10 +- src/Ryujinx.Graphics.Metal/Program.cs | 93 +++++++++++++------ 3 files changed, 73 insertions(+), 32 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3da0236d3..c005fe35c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index e621177d6..b6b811389 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -324,6 +324,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache bool loadHostCache = header.CodeGenVersion == CodeGenVersion; + if (context.Capabilities.Api == TargetApi.Metal) + { + loadHostCache = false; + } + int programIndex = 0; DataEntry entry = new(); @@ -630,7 +635,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return; } - WriteHostCode(context, hostCode, program.Shaders, streams, timestamp); + if (context.Capabilities.Api != TargetApi.Metal) + { + WriteHostCode(context, hostCode, program.Shaders, streams, timestamp); + } } /// diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 290bf7b9e..5ea305071 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -6,6 +6,7 @@ using SharpMetal.Metal; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -13,7 +14,11 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] class Program : IProgram { - private readonly ProgramLinkStatus _status; + private ProgramLinkStatus _status; + private ShaderSource[] _shaders; + private GCHandle[] _handles; + private int _successCount; + public MTLFunction VertexFunction; public MTLFunction FragmentFunction; public MTLFunction ComputeFunction; @@ -33,44 +38,64 @@ namespace Ryujinx.Graphics.Metal public Program(ShaderSource[] shaders, ResourceLayout resourceLayout, MTLDevice device, ComputeSize computeLocalSize = default) { ComputeLocalSize = computeLocalSize; + _shaders = shaders; + _handles = new GCHandle[_shaders.Length]; - for (int index = 0; index < shaders.Length; index++) + _status = ProgramLinkStatus.Incomplete; + + for (int i = 0; i < _shaders.Length; i++) { - ShaderSource shader = shaders[index]; + ShaderSource shader = _shaders[i]; var compileOptions = new MTLCompileOptions { PreserveInvariance = true }; + var index = i; - var libraryError = new NSError(IntPtr.Zero); - var shaderLibrary = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, ref libraryError); - if (libraryError != IntPtr.Zero) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code); - Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); - _status = ProgramLinkStatus.Failure; - return; - } - - switch (shaders[index].Stage) - { - case ShaderStage.Compute: - ComputeFunction = shaderLibrary.NewFunction(StringHelper.NSString("kernelMain")); - break; - case ShaderStage.Vertex: - VertexFunction = shaderLibrary.NewFunction(StringHelper.NSString("vertexMain")); - break; - case ShaderStage.Fragment: - FragmentFunction = shaderLibrary.NewFunction(StringHelper.NSString("fragmentMain")); - break; - default: - Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shaders[index].Stage}!"); - break; - } + _handles[i] = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, (library, error) => CompilationResultHandler(library, error, index)); } ClearSegments = BuildClearSegments(resourceLayout.Sets); (BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages); + } - _status = ProgramLinkStatus.Success; + public void CompilationResultHandler(MTLLibrary library, NSError error, int index) + { + var shader = _shaders[index]; + + if (_handles[index].IsAllocated) + { + _handles[index].Free(); + } + + if (error != IntPtr.Zero) + { + Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code); + Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(error.LocalizedDescription)}"); + _status = ProgramLinkStatus.Failure; + return; + } + + switch (_shaders[index].Stage) + { + case ShaderStage.Compute: + ComputeFunction = library.NewFunction(StringHelper.NSString("kernelMain")); + break; + case ShaderStage.Vertex: + VertexFunction = library.NewFunction(StringHelper.NSString("vertexMain")); + break; + case ShaderStage.Fragment: + FragmentFunction = library.NewFunction(StringHelper.NSString("fragmentMain")); + break; + default: + Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {_shaders[index].Stage}!"); + break; + } + + _successCount++; + + if (_successCount >= _shaders.Length && _status != ProgramLinkStatus.Failure) + { + _status = ProgramLinkStatus.Success; + } } private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection sets) @@ -218,12 +243,20 @@ namespace Ryujinx.Graphics.Metal public ProgramLinkStatus CheckProgramLink(bool blocking) { + if (blocking) + { + while (_status == ProgramLinkStatus.Incomplete) + { } + + return _status; + } + return _status; } public byte[] GetBinary() { - return ""u8.ToArray(); + return []; } public void AddGraphicsPipeline(ref PipelineUid key, MTLRenderPipelineState pipeline) -- 2.47.1 From 015f5d00b4f5b83d9111e05265a5829e84f49c2c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 12:13:40 +0100 Subject: [PATCH 294/368] Image shader gen support --- .../CodeGen/Msl/Declarations.cs | 26 ++++ .../CodeGen/Msl/Defaults.cs | 1 + .../CodeGen/Msl/Instructions/InstGen.cs | 4 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 133 +++++++++++++++++- .../CodeGen/Msl/MslGenerator.cs | 1 + src/Ryujinx.Graphics.Shader/SamplerType.cs | 4 +- 6 files changed, 162 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index d7475357c..3c92b0606 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -77,6 +77,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values, true); DeclareBufferStructures(context, context.Properties.StorageBuffers.Values, false); DeclareTextures(context, context.Properties.Textures.Values); + DeclareImages(context, context.Properties.Images.Values); if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) { @@ -270,6 +271,31 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(); } + private static void DeclareImages(CodeGenContext context, IEnumerable images) + { + context.AppendLine("struct Images"); + context.EnterScope(); + + List argBufferPointers = []; + + // TODO: Avoid Linq if we can + var sortedImages = images.OrderBy(x => x.Binding).ToArray(); + + foreach (TextureDefinition image in sortedImages) + { + var imageTypeName = image.Type.ToMslTextureType(true); + argBufferPointers.Add($"{imageTypeName} {image.Name};"); + } + + foreach (var pointer in argBufferPointers) + { + context.AppendLine(pointer); + } + + context.LeaveScope(";"); + context.AppendLine(); + } + private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) { if (context.Definitions.IaIndexing) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs index c01242ffe..f43f5f255 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs @@ -21,5 +21,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const uint ConstantBuffersIndex = 20; public const uint StorageBuffersIndex = 21; public const uint TexturesIndex = 22; + public const uint ImagesIndex = 23; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 8d4ef0e37..05fc3b2c8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -133,11 +133,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.GroupMemoryBarrier: return "|| FIND GROUP MEMORY BARRIER ||"; case Instruction.ImageLoad: - return "|| IMAGE LOAD ||"; case Instruction.ImageStore: - return "|| IMAGE STORE ||"; case Instruction.ImageAtomic: - return "|| IMAGE ATOMIC ||"; + return ImageLoadOrStore(context, operation); case Instruction.Load: return Load(context, operation); case Instruction.Lod: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index fce76012e..d13300e05 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; +using System.Text; using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -150,6 +151,129 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return varName; } + public static string ImageLoadOrStore(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + + var texCallBuilder = new StringBuilder(); + + string imageName = GetImageName(context.Properties, texOp); + texCallBuilder.Append($"images.{imageName}"); + texCallBuilder.Append('.'); + + if (texOp.Inst == Instruction.ImageAtomic) + { + texCallBuilder.Append((texOp.Flags & TextureFlags.AtomicMask) switch + { + TextureFlags.Add => "atomic_fetch_add", + TextureFlags.Minimum => "atomic_min", + TextureFlags.Maximum => "atomic_max", + TextureFlags.Increment => "atomic_fetch_add", + TextureFlags.Decrement => "atomic_fetch_sub", + TextureFlags.BitwiseAnd => "atomic_fetch_and", + TextureFlags.BitwiseOr => "atomic_fetch_or", + TextureFlags.BitwiseXor => "atomic_fetch_xor", + TextureFlags.Swap => "atomic_exchange", + TextureFlags.CAS => "atomic_compare_exchange_weak", + _ => "atomic_fetch_add", + }); + } + else + { + texCallBuilder.Append(texOp.Inst == Instruction.ImageLoad ? "read" : "write"); + } + + int srcIndex = 0; + + string Src(AggregateType type) + { + return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); + } + + texCallBuilder.Append('('); + + var coordsBuilder = new StringBuilder(); + + int coordsCount = texOp.Type.GetDimensions(); + + if (coordsCount > 1) + { + string[] elems = new string[coordsCount]; + + for (int index = 0; index < coordsCount; index++) + { + elems[index] = Src(AggregateType.S32); + } + + coordsBuilder.Append($"uint{coordsCount}({string.Join(", ", elems)})"); + } + else + { + coordsBuilder.Append(Src(AggregateType.S32)); + } + + if (isArray) + { + coordsBuilder.Append(", "); + coordsBuilder.Append(Src(AggregateType.S32)); + } + + if (texOp.Inst == Instruction.ImageStore) + { + AggregateType type = texOp.Format.GetComponentType(); + + string[] cElems = new string[4]; + + for (int index = 0; index < 4; index++) + { + if (srcIndex < texOp.SourcesCount) + { + cElems[index] = Src(type); + } + else + { + cElems[index] = type switch + { + AggregateType.S32 => NumberFormatter.FormatInt(0), + AggregateType.U32 => NumberFormatter.FormatUint(0), + _ => NumberFormatter.FormatFloat(0), + }; + } + } + + string prefix = type switch + { + AggregateType.S32 => "int", + AggregateType.U32 => "uint", + AggregateType.FP32 => "float", + _ => string.Empty, + }; + + texCallBuilder.Append($"{prefix}4({string.Join(", ", cElems)})"); + } + + texCallBuilder.Append(", "); + texCallBuilder.Append(coordsBuilder); + + if (texOp.Inst == Instruction.ImageAtomic) + { + // TODO: Finish atomic stuff + } + else + { + texCallBuilder.Append(')'); + + if (texOp.Inst == Instruction.ImageLoad) + { + texCallBuilder.Append(GetMaskMultiDest(texOp.Index)); + } + } + + return texCallBuilder.ToString(); + } + public static string Load(CodeGenContext context, AstOperation operation) { return GenerateLoadOrStore(context, operation, isStore: false); @@ -359,9 +483,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return texCall; } - private static string GetSamplerName(ShaderProperties resourceDefinitions, AstTextureOperation textOp) + private static string GetSamplerName(ShaderProperties resourceDefinitions, AstTextureOperation texOp) { - return resourceDefinitions.Textures[textOp.GetTextureSetAndBinding()].Name; + return resourceDefinitions.Textures[texOp.GetTextureSetAndBinding()].Name; + } + + private static string GetImageName(ShaderProperties resourceDefinitions, AstTextureOperation texOp) + { + return resourceDefinitions.Images[texOp.GetTextureSetAndBinding()].Name; } private static string GetMaskMultiDest(int mask) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 248b7159c..757abffdc 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -150,6 +150,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl args = args.Append($"constant ConstantBuffers &constant_buffers [[buffer({Defaults.ConstantBuffersIndex})]]").ToArray(); args = args.Append($"device StorageBuffers &storage_buffers [[buffer({Defaults.StorageBuffersIndex})]]").ToArray(); args = args.Append($"constant Textures &textures [[buffer({Defaults.TexturesIndex})]]").ToArray(); + args = args.Append($"constant Images &images [[buffer({Defaults.ImagesIndex})]]").ToArray(); } var funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}("; diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index 67c508012..44ff13294 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader return typeName; } - public static string ToMslTextureType(this SamplerType type) + public static string ToMslTextureType(this SamplerType type, bool image = false) { string typeName; @@ -192,7 +192,7 @@ namespace Ryujinx.Graphics.Shader typeName += "_array"; } - return typeName + ""; + return $"{typeName} "; } } } -- 2.47.1 From d42f0e594591b7231fba78f84b15bcc5544dfeaa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 14:58:56 +0100 Subject: [PATCH 295/368] Image binding support Kirby still has a problem with NaN 3D Texture --- src/Ryujinx.Graphics.Metal/Constants.cs | 2 +- src/Ryujinx.Graphics.Metal/EncoderState.cs | 13 +++ .../EncoderStateManager.cs | 82 ++++++++++++++++++- src/Ryujinx.Graphics.Metal/Pipeline.cs | 7 +- 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 76f4e0f87..58735824d 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -26,6 +26,6 @@ namespace Ryujinx.Graphics.Metal public const uint ConstantBuffersIndex = 20; public const uint StorageBuffersIndex = 21; public const uint TexturesIndex = 22; - public const uint ImagessIndex = 23; + public const uint ImagesIndex = 23; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index d7b9bb8c8..5ee8bd3d8 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -65,6 +65,18 @@ namespace Ryujinx.Graphics.Metal } } + record struct ImageRef + { + public ShaderStage Stage; + public Texture Storage; + + public ImageRef(ShaderStage stage, Texture storage) + { + Stage = stage; + Storage = storage; + } + } + struct PredrawState { public MTLCullMode CullMode; @@ -92,6 +104,7 @@ namespace Ryujinx.Graphics.Metal public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings]; + public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxTextureBindings]; public IndexBufferState IndexBuffer = default; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e3ace6e9a..463979dca 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -821,6 +821,20 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.Textures; } + public readonly void UpdateImage(ShaderStage stage, ulong binding, TextureBase texture) + { + if (texture is Texture view) + { + _currentState.ImageRefs[binding] = new(stage, view); + } + else + { + _currentState.ImageRefs[binding] = default; + } + + _currentState.Dirty |= DirtyFlags.Images; + } + private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); @@ -1171,7 +1185,42 @@ namespace Ryujinx.Graphics.Metal } break; case MetalRenderer.ImageSetIndex: - // TODO: Images + if (!segment.IsArray) + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref var image = ref _currentState.ImageRefs[index]; + + var storage = image.Storage; + + if (storage == null) + { + continue; + } + + var mtlTexture = storage.GetHandle(); + + MTLRenderStages renderStages = 0; + + if ((segment.Stages & ResourceStages.Vertex) != 0) + { + vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIdIndex++; + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if ((segment.Stages & ResourceStages.Fragment) != 0) + { + fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIdIndex++; + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages); + } + } break; } } @@ -1336,7 +1385,34 @@ namespace Ryujinx.Graphics.Metal } break; case MetalRenderer.ImageSetIndex: - // TODO: Images + if (!segment.IsArray) + { + if (segment.Type != ResourceType.BufferTexture) + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref var image = ref _currentState.ImageRefs[index]; + + var storage = image.Storage; + + if (storage == null) + { + continue; + } + + var mtlTexture = storage.GetHandle(); + + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); + resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resourceIdIndex++; + } + } + } + } break; } } @@ -1356,7 +1432,7 @@ namespace Ryujinx.Graphics.Metal MetalRenderer.UniformSetIndex => Constants.ConstantBuffersIndex, MetalRenderer.StorageSetIndex => Constants.StorageBuffersIndex, MetalRenderer.TextureSetIndex => Constants.TexturesIndex, - MetalRenderer.ImageSetIndex => Constants.ImagessIndex, + MetalRenderer.ImageSetIndex => Constants.ImagesIndex, }; } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index d5c04884a..00ca85b56 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -497,7 +497,12 @@ namespace Ryujinx.Graphics.Metal public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (texture is TextureBase tex) + { + var index = (ulong)binding; + + _encoderStateManager.UpdateImage(stage, index, tex); + } } public void SetImageArray(ShaderStage stage, int binding, IImageArray array) -- 2.47.1 From 6229f3bb4cc3cb5cf8b9689da89a541834d1c53a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 15:23:16 +0100 Subject: [PATCH 296/368] Shader Memory Barriers Fixes some of the shader generation failures in Sonic Frontiers --- .../CodeGen/Msl/Instructions/InstGen.cs | 5 ++--- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 05fc3b2c8..f9601f62c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -131,7 +131,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.FSIEnd: return "|| FSI END ||"; case Instruction.GroupMemoryBarrier: - return "|| FIND GROUP MEMORY BARRIER ||"; + case Instruction.MemoryBarrier: + return MemoryBarrier(context, operation); case Instruction.ImageLoad: case Instruction.ImageStore: case Instruction.ImageAtomic: @@ -140,8 +141,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return Load(context, operation); case Instruction.Lod: return Lod(context, operation); - case Instruction.MemoryBarrier: - return "|| MEMORY BARRIER ||"; case Instruction.Store: return Store(context, operation); case Instruction.SwizzleAdd: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index d13300e05..04f887d56 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -600,6 +600,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return $"float2(as_type({srcExpr})){GetMask(operation.Index)}"; } + public static string MemoryBarrier(CodeGenContext context, AstOperation operation) + { + var grouped = (operation.Inst & Instruction.Mask) == Instruction.GroupMemoryBarrier; + + return $"threadgroup_barrier(mem_flags::mem_{(grouped ? "threadgroup" : "device")})"; + } + private static string GetMask(int index) { return $".{"xy".AsSpan(index, 1)}"; -- 2.47.1 From 159afd5d0314ee6e8c6467aeafa71b2a70f988b0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 15:35:50 +0100 Subject: [PATCH 297/368] Consolodate barriers --- .../CodeGen/Msl/Instructions/InstGen.cs | 5 ++--- .../CodeGen/Msl/Instructions/InstGenBarrier.cs | 9 ++++----- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 7 ------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index f9601f62c..887dfae4f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -122,8 +122,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { case Instruction.Ballot: return Ballot(context, operation); - case Instruction.Barrier: - return Barrier(context, operation); case Instruction.Call: return Call(context, operation); case Instruction.FSIBegin: @@ -132,7 +130,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return "|| FSI END ||"; case Instruction.GroupMemoryBarrier: case Instruction.MemoryBarrier: - return MemoryBarrier(context, operation); + case Instruction.Barrier: + return Barrier(context, operation); case Instruction.ImageLoad: case Instruction.ImageStore: case Instruction.ImageAtomic: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs index 7d681de26..77f05defc 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs @@ -1,8 +1,5 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { @@ -10,7 +7,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { public static string Barrier(CodeGenContext context, AstOperation operation) { - return "threadgroup_barrier(mem_flags::mem_threadgroup)"; + var device = (operation.Inst & Instruction.Mask) == Instruction.MemoryBarrier; + + return $"threadgroup_barrier(mem_flags::mem_{(device ? "device" : "threadgroup")})"; } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 04f887d56..d13300e05 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -600,13 +600,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return $"float2(as_type({srcExpr})){GetMask(operation.Index)}"; } - public static string MemoryBarrier(CodeGenContext context, AstOperation operation) - { - var grouped = (operation.Inst & Instruction.Mask) == Instruction.GroupMemoryBarrier; - - return $"threadgroup_barrier(mem_flags::mem_{(grouped ? "threadgroup" : "device")})"; - } - private static string GetMask(int index) { return $".{"xy".AsSpan(index, 1)}"; -- 2.47.1 From 69bee52a89f131ccf53069954b9c7bfd10134102 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 15:43:53 +0100 Subject: [PATCH 298/368] SwizzleAdd (NOT TESTED) --- .../CodeGen/Msl/Declarations.cs | 10 +++++----- .../CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs | 1 + .../CodeGen/Msl/HelperFunctions/SwizzleAdd.metal | 7 +++++++ .../CodeGen/Msl/Instructions/InstGen.cs | 8 ++++++-- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 2 +- .../Ryujinx.Graphics.Shader.csproj | 1 + 6 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 3c92b0606..5e9f6a3b0 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -65,11 +65,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine("using namespace metal;"); context.AppendLine(); - if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) - { - - } - DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); context.AppendLine(); DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); @@ -93,6 +88,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal"); } + + if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) + { + AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal"); + } } static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs index a48da4990..370159a0e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs @@ -5,5 +5,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public static string FindLSB = "findLSB"; public static string FindMSBS32 = "findMSBS32"; public static string FindMSBU32 = "findMSBU32"; + public static string SwizzleAdd = "swizzleAdd"; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal new file mode 100644 index 000000000..22a079b01 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal @@ -0,0 +1,7 @@ +float swizzleAdd(float x, float y, int mask, uint thread_index_in_simdgroup) +{ + float4 xLut = float4(1.0, -1.0, 1.0, 0.0); + float4 yLut = float4(1.0, 1.0, -1.0, 1.0); + int lutIdx = (mask >> (int(thread_index_in_simdgroup & 3u) * 2)) & 3; + return x * xLut[lutIdx] + y * yLut[lutIdx]; +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 887dfae4f..5efc8ee1b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -69,6 +69,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions builder.Append(GetSourceExpr(context, operation.GetSource(argIndex), dstType)); } + + if ((operation.Inst & Instruction.Mask) == Instruction.SwizzleAdd) + { + // SwizzleAdd takes one last argument, the thread_index_in_simdgroup + builder.Append(", thread_index_in_simdgroup"); + } } return $"{info.OpName}({builder})"; @@ -142,8 +148,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return Lod(context, operation); case Instruction.Store: return Store(context, operation); - case Instruction.SwizzleAdd: - return "|| SWIZZLE ADD ||"; case Instruction.TextureSample: return TextureSample(context, operation); case Instruction.TextureQuerySamples: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 68ec872af..596f95ddd 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); Add(Instruction.Store, InstType.Special); Add(Instruction.Subtract, InstType.OpBinary, "-", 2); - Add(Instruction.SwizzleAdd, InstType.Special); + Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); Add(Instruction.TextureSample, InstType.Special); Add(Instruction.TextureQuerySamples, InstType.Special); Add(Instruction.TextureQuerySize, InstType.Special); diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 7a3180788..f702aae9b 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -19,5 +19,6 @@ + -- 2.47.1 From 0064afeb6a356f96144209b1960397ac4d4960c0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 16:25:03 +0100 Subject: [PATCH 299/368] FSI (with raster order groups) --- .../CodeGen/Msl/Declarations.cs | 19 ++++++++++++------- .../CodeGen/Msl/Instructions/InstGen.cs | 3 +-- .../CodeGen/Msl/Instructions/InstGenHelper.cs | 4 ++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 5e9f6a3b0..7428c3a31 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -65,14 +65,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine("using namespace metal;"); context.AppendLine(); + var fsi = (info.HelperFunctionsMask & HelperFunctionsMask.FSI) != 0; + DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); context.AppendLine(); DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); context.AppendLine(); - DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values, true); - DeclareBufferStructures(context, context.Properties.StorageBuffers.Values, false); + DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values, true, fsi); + DeclareBufferStructures(context, context.Properties.StorageBuffers.Values, false, fsi); DeclareTextures(context, context.Properties.Textures.Values); - DeclareImages(context, context.Properties.Images.Values); + DeclareImages(context, context.Properties.Images.Values, fsi); if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) { @@ -180,7 +182,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } - private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers, bool constant) + private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers, bool constant, bool fsi) { var name = constant ? "ConstantBuffers" : "StorageBuffers"; var addressSpace = constant ? "constant" : "device"; @@ -193,8 +195,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (BufferDefinition buffer in sortedBuffers) { var needsPadding = buffer.Layout == BufferLayout.Std140; + string fsiSuffix = constant && fsi ? " [[raster_order_group(0)]]" : ""; - argBufferPointers.Add($"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};"); + argBufferPointers.Add($"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};"); context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); context.EnterScope(); @@ -271,7 +274,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(); } - private static void DeclareImages(CodeGenContext context, IEnumerable images) + private static void DeclareImages(CodeGenContext context, IEnumerable images, bool fsi) { context.AppendLine("struct Images"); context.EnterScope(); @@ -284,7 +287,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (TextureDefinition image in sortedImages) { var imageTypeName = image.Type.ToMslTextureType(true); - argBufferPointers.Add($"{imageTypeName} {image.Name};"); + string fsiSuffix = fsi ? " [[raster_order_group(0)]]" : ""; + + argBufferPointers.Add($"{imageTypeName} {image.Name}{fsiSuffix};"); } foreach (var pointer in argBufferPointers) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 5efc8ee1b..daeb62221 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -131,9 +131,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions case Instruction.Call: return Call(context, operation); case Instruction.FSIBegin: - return "|| FSI BEGIN ||"; case Instruction.FSIEnd: - return "|| FSI END ||"; + return "// FSI implemented with raster order groups in MSL"; case Instruction.GroupMemoryBarrier: case Instruction.MemoryBarrier: case Instruction.Barrier: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs index 596f95ddd..49f3c63aa 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs @@ -164,8 +164,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions public static bool NeedsParenthesis(IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs) { - // If the node isn't a operation, then it can only be a operand, - // and those never needs to be surrounded in parenthesis. + // If the node isn't an operation, then it can only be an operand, + // and those never needs to be surrounded in parentheses. if (node is not AstOperation operation) { // This is sort of a special case, if this is a negative constant, -- 2.47.1 From f5b82cd6dc3fc5607651ad6afd75fb75663e618b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 21:02:19 +0100 Subject: [PATCH 300/368] Fix image bindings --- .../EncoderStateManager.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 463979dca..db7b81dce 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -234,10 +234,10 @@ namespace Ryujinx.Graphics.Metal UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.TextureSetIndex); } - // if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) - // { - // UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.ImageSetIndex); - // } + if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) + { + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.ImageSetIndex); + } _currentState.Dirty &= ~DirtyFlags.RenderAll; } @@ -264,10 +264,10 @@ namespace Ryujinx.Graphics.Metal UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.TextureSetIndex); } - // if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) - // { - // UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.ImageSetIndex); - // } + if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) + { + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.ImageSetIndex); + } _currentState.Dirty &= ~DirtyFlags.ComputeAll; } -- 2.47.1 From 4373610790469356e7ba5da55826c73b161b70c9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 21:53:17 +0100 Subject: [PATCH 301/368] Multisample Blits Partially fixes Sonic Colors Ultimate --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 31 +++++++++++-- src/Ryujinx.Graphics.Metal/Pipeline.cs | 15 ++----- .../Ryujinx.Graphics.Metal.csproj | 1 + src/Ryujinx.Graphics.Metal/Shaders/Blit.metal | 6 +-- .../Shaders/BlitMs.metal | 45 +++++++++++++++++++ src/Ryujinx.Graphics.Metal/Texture.cs | 8 +--- 6 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 647e4ba8b..270a628dc 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; @@ -21,6 +22,7 @@ namespace Ryujinx.Graphics.Metal private readonly ISampler _samplerLinear; private readonly ISampler _samplerNearest; private readonly IProgram _programColorBlit; + private readonly IProgram _programColorBlitMs; private readonly List _programsColorClear = new(); private readonly IProgram _programDepthStencilClear; private readonly IProgram _programStrideChange; @@ -47,6 +49,13 @@ namespace Ryujinx.Graphics.Metal new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) ], blitResourceLayout, device); + var blitMsSource = ReadMsl("BlitMs.metal"); + _programColorBlitMs = new Program( + [ + new ShaderSource(blitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitMsSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); + var colorClearResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build(); @@ -87,8 +96,8 @@ namespace Ryujinx.Graphics.Metal public unsafe void BlitColor( CommandBufferScoped cbs, - ITexture src, - ITexture dst, + Texture src, + Texture dst, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter, @@ -140,7 +149,23 @@ namespace Ryujinx.Graphics.Metal 0f, 1f); - _pipeline.SetProgram(_programColorBlit); + bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + + if (dstIsDepthOrStencil) + { + // TODO: Depth & stencil blit! + Logger.Warning?.PrintMsg(LogClass.Gpu, "Requested a depth or stencil blit!"); + _pipeline.SwapState(null); + return; + } + else if (src.Info.Target.IsMultisample()) + { + _pipeline.SetProgram(_programColorBlitMs); + } + else + { + _pipeline.SetProgram(_programColorBlit); + } int dstWidth = dst.Width; int dstHeight = dst.Height; diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 00ca85b56..00020267b 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -193,22 +193,13 @@ namespace Ryujinx.Graphics.Metal } public void Blit( - ITexture src, - ITexture dst, + Texture src, + Texture dst, Extents2D srcRegion, Extents2D dstRegion, - bool isDepthOrStencil, bool linearFilter) { - if (isDepthOrStencil) - { - // TODO: Depth & stencil blit! - Logger.Warning?.PrintMsg(LogClass.Gpu, "Requested a depth or stencil blit!"); - } - else - { - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); - } + _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); } public void Barrier() diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index f4e98cd45..8b6de05f3 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index 37962bbf7..a5e4e8170 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -12,7 +12,7 @@ struct TexCoords { }; struct ConstantBuffers { - constant TexCoords* texCoord; + constant TexCoords* tex_coord; }; struct Textures @@ -27,8 +27,8 @@ vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], int low = vid & 1; int high = vid >> 1; - out.uv.x = constant_buffers.texCoord->data[low]; - out.uv.y = constant_buffers.texCoord->data[2 + high]; + out.uv.x = constant_buffers.tex_coord->data[low]; + out.uv.y = constant_buffers.tex_coord->data[2 + high]; out.position.x = (float(low) - 0.5f) * 2.0f; out.position.y = (float(high) - 0.5f) * 2.0f; out.position.z = 0.0f; diff --git a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal new file mode 100644 index 000000000..b8e01ffe8 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal @@ -0,0 +1,45 @@ +#include + +using namespace metal; + +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; + +struct TexCoords { + float data[4]; +}; + +struct ConstantBuffers { + constant TexCoords* tex_coord; +}; + +struct Textures +{ + texture2d_ms texture; +}; + +vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], + constant ConstantBuffers &constant_buffers [[buffer(20)]]) { + CopyVertexOut out; + + int low = vid & 1; + int high = vid >> 1; + out.uv.x = constant_buffers.tex_coord->data[low]; + out.uv.y = constant_buffers.tex_coord->data[2 + high]; + out.position.x = (float(low) - 0.5f) * 2.0f; + out.position.y = (float(high) - 0.5f) * 2.0f; + out.position.z = 0.0f; + out.position.w = 1.0f; + + return out; +} + +fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], + constant Textures &textures [[buffer(22)]], + uint sample_id [[sample_id]]) { + uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); + uint2 tex_coord = uint2(in.uv * float2(tex_size)); + return textures.texture.read(tex_coord, sample_id); +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index fa5def60b..9392b5e6d 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -249,14 +249,8 @@ namespace Ryujinx.Graphics.Metal } var dst = (Texture)destination; - bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); - if (dst.Info.IsCompressed) - { - Console.WriteLine("shit"); - } - - Pipeline.Blit(this, destination, srcRegion, dstRegion, isDepthOrStencil, linearFilter); + Pipeline.Blit(this, dst, srcRegion, dstRegion, linearFilter); } public void CopyTo(BufferRange range, int layer, int level, int stride) -- 2.47.1 From 40ea153616f5585ed95eb75a089926b8e6b5c0aa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 23:27:59 +0100 Subject: [PATCH 302/368] DepthStencil Blits --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 183 +++++++++++++++++- src/Ryujinx.Graphics.Metal/Pipeline.cs | 12 +- .../Ryujinx.Graphics.Metal.csproj | 4 + .../Shaders/BlitMs.metal | 24 --- .../Shaders/DepthBlit.metal | 27 +++ .../Shaders/DepthBlitMs.metal | 29 +++ .../Shaders/StencilBlit.metal | 27 +++ .../Shaders/StencilBlitMs.metal | 29 +++ src/Ryujinx.Graphics.Metal/Texture.cs | 4 +- 9 files changed, 310 insertions(+), 29 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 270a628dc..ed9a7f656 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -26,6 +26,10 @@ namespace Ryujinx.Graphics.Metal private readonly List _programsColorClear = new(); private readonly IProgram _programDepthStencilClear; private readonly IProgram _programStrideChange; + private readonly IProgram _programDepthBlit; + private readonly IProgram _programDepthBlitMs; + private readonly IProgram _programStencilBlit; + private readonly IProgram _programStencilBlitMs; private readonly EncoderState _helperShaderState = new(); @@ -53,7 +57,7 @@ namespace Ryujinx.Graphics.Metal _programColorBlitMs = new Program( [ new ShaderSource(blitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitMsSource, ShaderStage.Vertex, TargetLanguage.Msl) + new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) ], blitResourceLayout, device); var colorClearResourceLayout = new ResourceLayoutBuilder() @@ -87,6 +91,34 @@ namespace Ryujinx.Graphics.Metal [ new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) ], strideChangeResourceLayout, device, new ComputeSize(64, 1, 1)); + + var depthBlitSource = ReadMsl("DepthBlit.metal"); + _programDepthBlit = new Program( + [ + new ShaderSource(depthBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); + + var depthBlitMsSource = ReadMsl("DepthBlitMs.metal"); + _programDepthBlitMs = new Program( + [ + new ShaderSource(depthBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); + + var stencilBlitSource = ReadMsl("StencilBlit.metal"); + _programStencilBlit = new Program( + [ + new ShaderSource(stencilBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); + + var stencilBlitMsSource = ReadMsl("StencilBlitMs.metal"); + _programStencilBlitMs = new Program( + [ + new ShaderSource(stencilBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); } private static string ReadMsl(string fileName) @@ -193,6 +225,155 @@ namespace Ryujinx.Graphics.Metal _pipeline.SwapState(null); } + public unsafe void BlitDepthStencil( + CommandBufferScoped cbs, + Texture src, + Texture dst, + Extents2D srcRegion, + Extents2D dstRegion) + { + _pipeline.SwapState(_helperShaderState); + + const int RegionBufferSize = 16; + + Span region = stackalloc float[RegionBufferSize / sizeof(float)]; + + region[0] = srcRegion.X1 / (float)src.Width; + region[1] = srcRegion.X2 / (float)src.Width; + region[2] = srcRegion.Y1 / (float)src.Height; + region[3] = srcRegion.Y2 / (float)src.Height; + + if (dstRegion.X1 > dstRegion.X2) + { + (region[0], region[1]) = (region[1], region[0]); + } + + if (dstRegion.Y1 > dstRegion.Y2) + { + (region[2], region[3]) = (region[3], region[2]); + } + + using var buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize); + buffer.Holder.SetDataUnchecked(buffer.Offset, region); + _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); + + Span viewports = stackalloc Viewport[16]; + + var rect = new Rectangle( + MathF.Min(dstRegion.X1, dstRegion.X2), + MathF.Min(dstRegion.Y1, dstRegion.Y2), + MathF.Abs(dstRegion.X2 - dstRegion.X1), + MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); + + viewports[0] = new Viewport( + rect, + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + int dstWidth = dst.Width; + int dstHeight = dst.Height; + + Span> scissors = stackalloc Rectangle[16]; + + scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); + + _pipeline.SetRenderTargets([], dst); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + + if (src.Info.Format is + Format.D16Unorm or + Format.D32Float or + Format.X8UintD24Unorm or + Format.D24UnormS8Uint or + Format.D32FloatS8Uint or + Format.S8UintD24Unorm) + { + var depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth); + + BlitDepthStencilDraw(depthTexture, isDepth: true); + + if (depthTexture != src) + { + depthTexture.Release(); + } + } + + if (src.Info.Format is + Format.S8Uint or + Format.D24UnormS8Uint or + Format.D32FloatS8Uint or + Format.S8UintD24Unorm) + { + var stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil); + + BlitDepthStencilDraw(stencilTexture, isDepth: false); + + if (stencilTexture != src) + { + stencilTexture.Release(); + } + } + + // Restore previous state + _pipeline.SwapState(null); + } + + private static Texture CreateDepthOrStencilView(Texture depthStencilTexture, DepthStencilMode depthStencilMode) + { + if (depthStencilTexture.Info.DepthStencilMode == depthStencilMode) + { + return depthStencilTexture; + } + + return (Texture)depthStencilTexture.CreateView(new TextureCreateInfo( + depthStencilTexture.Info.Width, + depthStencilTexture.Info.Height, + depthStencilTexture.Info.Depth, + depthStencilTexture.Info.Levels, + depthStencilTexture.Info.Samples, + depthStencilTexture.Info.BlockWidth, + depthStencilTexture.Info.BlockHeight, + depthStencilTexture.Info.BytesPerPixel, + depthStencilTexture.Info.Format, + depthStencilMode, + depthStencilTexture.Info.Target, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha), 0, 0); + } + + private void BlitDepthStencilDraw(Texture src, bool isDepth) + { + if (isDepth) + { + _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); + } + else + { + _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit); + _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); + } + + _pipeline.Draw(4, 1, 0, 0); + + if (isDepth) + { + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); + } + else + { + _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); + } + } + public unsafe void DrawTexture( ITexture src, ISampler srcSampler, diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 00020267b..a20d33e00 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -197,9 +197,17 @@ namespace Ryujinx.Graphics.Metal Texture dst, Extents2D srcRegion, Extents2D dstRegion, + bool isDepthOrStencil, bool linearFilter) { - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); + if (isDepthOrStencil) + { + _renderer.HelperShader.BlitDepthStencil(Cbs, src, dst, srcRegion, dstRegion); + } + else + { + _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); + } } public void Barrier() @@ -258,10 +266,8 @@ namespace Ryujinx.Graphics.Metal { var depthStencil = _encoderStateManager.DepthStencil; - // TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format if (depthStencil == null) { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Attempted to clear invalid depth stencil!"); return; } diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 8b6de05f3..8cbdc8bcb 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -20,6 +20,10 @@ + + + + diff --git a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal index b8e01ffe8..09c5d76ca 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal @@ -7,35 +7,11 @@ struct CopyVertexOut { float2 uv; }; -struct TexCoords { - float data[4]; -}; - -struct ConstantBuffers { - constant TexCoords* tex_coord; -}; - struct Textures { texture2d_ms texture; }; -vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], - constant ConstantBuffers &constant_buffers [[buffer(20)]]) { - CopyVertexOut out; - - int low = vid & 1; - int high = vid >> 1; - out.uv.x = constant_buffers.tex_coord->data[low]; - out.uv.y = constant_buffers.tex_coord->data[2 + high]; - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], constant Textures &textures [[buffer(22)]], uint sample_id [[sample_id]]) { diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal new file mode 100644 index 000000000..c6b547be8 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal @@ -0,0 +1,27 @@ +#include + +using namespace metal; + +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; + +struct Textures +{ + texture2d texture; + sampler sampler; +}; + +struct FragmentOut { + float depth [[depth(any)]]; +}; + +fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], + constant Textures &textures [[buffer(22)]]) { + FragmentOut out; + + out.depth = textures.texture.sample(textures.sampler, in.uv).r; + + return out; +} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal new file mode 100644 index 000000000..9fb5e6e50 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal @@ -0,0 +1,29 @@ +#include + +using namespace metal; + +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; + +struct Textures +{ + texture2d_ms texture; +}; + +struct FragmentOut { + float depth [[depth(any)]]; +}; + +fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], + constant Textures &textures [[buffer(22)]], + uint sample_id [[sample_id]]) { + FragmentOut out; + + uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); + uint2 tex_coord = uint2(in.uv * float2(tex_size)); + out.depth = textures.texture.read(tex_coord, sample_id).r; + + return out; +} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal new file mode 100644 index 000000000..da7c6e90a --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal @@ -0,0 +1,27 @@ +#include + +using namespace metal; + +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; + +struct Textures +{ + texture2d texture; + sampler sampler; +}; + +struct FragmentOut { + uint stencil [[stencil]]; +}; + +fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], + constant Textures &textures [[buffer(22)]]) { + FragmentOut out; + + out.stencil = textures.texture.sample(textures.sampler, in.uv).r; + + return out; +} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal new file mode 100644 index 000000000..3948eacc7 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal @@ -0,0 +1,29 @@ +#include + +using namespace metal; + +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; + +struct Textures +{ + texture2d_ms texture; +}; + +struct FragmentOut { + uint stencil [[stencil]]; +}; + +fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], + constant Textures &textures [[buffer(22)]], + uint sample_id [[sample_id]]) { + FragmentOut out; + + uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); + uint2 tex_coord = uint2(in.uv * float2(tex_size)); + out.stencil = textures.texture.read(tex_coord, sample_id).r; + + return out; +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 9392b5e6d..5ca92eb1e 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -250,7 +250,9 @@ namespace Ryujinx.Graphics.Metal var dst = (Texture)destination; - Pipeline.Blit(this, dst, srcRegion, dstRegion, linearFilter); + bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + + Pipeline.Blit(this, dst, srcRegion, dstRegion, isDepthOrStencil, linearFilter); } public void CopyTo(BufferRange range, int layer, int level, int stride) -- 2.47.1 From ffb9040b3b73b59d24142e87a20ef5d8de8386d6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 25 Jul 2024 12:22:08 +0100 Subject: [PATCH 303/368] Shader Gen Fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes Luigi’s Mansion 2 HD --- .../CodeGen/Msl/Declarations.cs | 7 ++++-- .../CodeGen/Msl/Defaults.cs | 2 ++ .../CodeGen/Msl/Instructions/InstGenCall.cs | 24 ++++++++++++++++--- .../CodeGen/Msl/Instructions/IoMap.cs | 2 +- .../CodeGen/Msl/MslGenerator.cs | 21 ++++++++++++++-- 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 7428c3a31..3c9859a2f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -112,12 +112,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl switch (stage) { case ShaderStage.Vertex: - context.AppendLine("VertexOut out;"); + context.AppendLine("VertexOut out = {};"); // TODO: Only add if necessary context.AppendLine("uint instance_index = instance_id + base_instance;"); break; case ShaderStage.Fragment: - context.AppendLine("FragmentOut out;"); + context.AppendLine("FragmentOut out = {};"); break; } @@ -420,6 +420,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.PointSize => "float", IoVariable.FragmentOutputColor => GetVarTypeName(context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), IoVariable.FragmentOutputDepth => "float", + IoVariable.ClipDistance => "float", _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) }; string name = ioDefinition.IoVariable switch @@ -428,6 +429,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.PointSize => "point_size", IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", IoVariable.FragmentOutputDepth => "depth", + IoVariable.ClipDistance => "clip_distance", _ => $"{Defaults.OAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch @@ -437,6 +439,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", IoVariable.FragmentOutputDepth => "[[depth(any)]]", + IoVariable.ClipDistance => $"[[clip_distance]][{Defaults.TotalClipDistances}]", _ => "" }; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs index f43f5f255..a78de36ce 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs @@ -22,5 +22,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const uint StorageBuffersIndex = 21; public const uint TexturesIndex = 22; public const uint ImagesIndex = 23; + + public const int TotalClipDistances = 8; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs index 0bad36f73..44881deee 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs @@ -10,10 +10,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { AstOperand funcId = (AstOperand)operation.GetSource(0); - var functon = context.GetFunction(funcId.Value); + var function = context.GetFunction(funcId.Value); int argCount = operation.SourcesCount - 1; int additionalArgCount = CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); + bool needsThreadIndex = false; + + // TODO: Replace this with a proper flag + if (function.Name.Contains("Shuffle")) + { + needsThreadIndex = true; + additionalArgCount++; + } string[] args = new string[argCount + additionalArgCount]; @@ -23,20 +31,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions args[0] = "in"; args[1] = "constant_buffers"; args[2] = "storage_buffers"; + + if (needsThreadIndex) + { + args[3] = "thread_index_in_simdgroup"; + } } else { args[0] = "constant_buffers"; args[1] = "storage_buffers"; + + if (needsThreadIndex) + { + args[2] = "thread_index_in_simdgroup"; + } } int argIndex = additionalArgCount; for (int i = 0; i < argCount; i++) { - args[argIndex++] = GetSourceExpr(context, operation.GetSource(i + 1), functon.GetArgumentType(i)); + args[argIndex++] = GetSourceExpr(context, operation.GetSource(i + 1), function.GetArgumentType(i)); } - return $"{functon.Name}({string.Join(", ", args)})"; + return $"{function.Name}({string.Join(", ", args)})"; } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index bd7a15e2f..57c180fb4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.BaseInstance => ("base_instance", AggregateType.U32), IoVariable.BaseVertex => ("base_vertex", AggregateType.U32), IoVariable.CtaId => ("threadgroup_position_in_grid", AggregateType.Vector3 | AggregateType.U32), - IoVariable.ClipDistance => ("clip_distance", AggregateType.Array | AggregateType.FP32), + IoVariable.ClipDistance => ("out.clip_distance", AggregateType.Array | AggregateType.FP32), IoVariable.FragmentOutputColor => ($"out.color{location}", definitions.GetFragmentOutputColorType(location)), IoVariable.FragmentOutputDepth => ("out.depth", AggregateType.FP32), IoVariable.FrontFacing => ("in.front_facing", AggregateType.Bool), diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 757abffdc..28a69c508 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -64,6 +64,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl bool isMainFunc = false) { int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); + bool needsThreadIndex = false; + + // TODO: Replace this with a proper flag + if (function.Name.Contains("Shuffle")) + { + needsThreadIndex = true; + additionalArgCount++; + } string[] args = new string[additionalArgCount + function.InArguments.Length + function.OutArguments.Length]; @@ -75,11 +83,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl args[0] = stage == ShaderStage.Vertex ? "VertexIn in" : "FragmentIn in"; args[1] = "constant ConstantBuffers &constant_buffers"; args[2] = "device StorageBuffers &storage_buffers"; + + if (needsThreadIndex) + { + args[3] = "uint thread_index_in_simdgroup"; + } } else { args[0] = "constant ConstantBuffers &constant_buffers"; args[1] = "device StorageBuffers &storage_buffers"; + + if (needsThreadIndex) + { + args[2] = "uint thread_index_in_simdgroup"; + } } } @@ -93,8 +111,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { int j = i + function.InArguments.Length; - // Likely need to be made into pointers - args[argIndex++] = $"out {Declarations.GetVarTypeName(function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; + args[argIndex++] = $"thread {Declarations.GetVarTypeName(function.OutArguments[i])} &{OperandManager.GetArgumentName(j)}"; } string funcKeyword = "inline"; -- 2.47.1 From cb0a1ce48a9356854dcfdcc24ddbdf3da5e5139b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 25 Jul 2024 18:58:26 +0100 Subject: [PATCH 304/368] Stop complaining about clip distance --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index a20d33e00..2d70903cf 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -628,7 +628,7 @@ namespace Ryujinx.Graphics.Metal public void SetUserClipDistance(int index, bool enableClip) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + // TODO. Same as Vulkan } public void SetVertexAttribs(ReadOnlySpan vertexAttribs) -- 2.47.1 From 5220ee1dc877db4f6b1da029ce924bddb0b5134b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 26 Jul 2024 13:58:25 +0100 Subject: [PATCH 305/368] Some debug improvements --- src/Ryujinx.Graphics.Metal/CommandBufferPool.cs | 7 ++++++- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 1 - src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs index 050f93efc..5e7576b37 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs @@ -35,7 +35,12 @@ namespace Ryujinx.Graphics.Metal public void Use(MTLCommandQueue queue, IEncoderFactory stateManager) { - CommandBuffer = queue.CommandBuffer(); + MTLCommandBufferDescriptor descriptor = new(); +#if DEBUG + descriptor.ErrorOptions = MTLCommandBufferErrorOption.EncoderExecutionStatus; +#endif + + CommandBuffer = queue.CommandBuffer(descriptor); Fence = new FenceHolder(CommandBuffer); Encoders.Initialize(CommandBuffer, stateManager); diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index 18662e21a..091de7ef8 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -137,7 +137,6 @@ namespace Ryujinx.Graphics.Metal }; } - // TODO: Metal does not have native support for Triangle Fans but it is possible to emulate with TriangleStrip and moving around the indices public static MTLPrimitiveType Convert(this PrimitiveTopology topology) { return topology switch diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 3c9859a2f..346beb02e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -59,6 +59,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public static void Declare(CodeGenContext context, StructuredProgramInfo info) { + // TODO: Re-enable this warning + context.AppendLine("#pragma clang diagnostic ignored \"-Wunused-variable\""); + context.AppendLine(); context.AppendLine("#include "); context.AppendLine("#include "); context.AppendLine(); -- 2.47.1 From 7bf0625075f7cfdd93c798a79970d6d343c2a23e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 26 Jul 2024 17:12:31 +0100 Subject: [PATCH 306/368] Fix compute generation failure in NieR --- .../CodeGen/Msl/Instructions/InstGen.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index daeb62221..ac294d960 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -49,7 +49,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions ? AggregateType.S32 : AggregateType.U32; - builder.Append($"(device {Declarations.GetVarTypeName(dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); + var shared = operation.StorageKind == StorageKind.SharedMemory; + + builder.Append($"({(shared ? "threadgroup" : "device")} {Declarations.GetVarTypeName(dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++) { -- 2.47.1 From eeadf17f5c8763f1ab3fd08ee2a4c86d08df52c3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jul 2024 11:31:29 +0100 Subject: [PATCH 307/368] Remove ClearSegments for now Currently unimplemented and issues are arising with building BindingSegments in general. --- src/Ryujinx.Graphics.Metal/Program.cs | 58 --------------------------- 1 file changed, 58 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 5ea305071..c1eae2273 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -28,7 +28,6 @@ namespace Ryujinx.Graphics.Metal private MTLComputePipelineState? _computePipelineCache; private bool _firstBackgroundUse; - public ResourceBindingSegment[][] ClearSegments { get; } public ResourceBindingSegment[][] BindingSegments { get; } // Argument buffer sizes for Vertex or Compute stages public int[] ArgumentBufferSizes { get; } @@ -53,7 +52,6 @@ namespace Ryujinx.Graphics.Metal _handles[i] = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, (library, error) => CompilationResultHandler(library, error, index)); } - ClearSegments = BuildClearSegments(resourceLayout.Sets); (BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages); } @@ -98,62 +96,6 @@ namespace Ryujinx.Graphics.Metal } } - private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection sets) - { - ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][]; - - for (int setIndex = 0; setIndex < sets.Count; setIndex++) - { - List currentSegments = new(); - - ResourceDescriptor currentDescriptor = default; - int currentCount = 0; - - for (int index = 0; index < sets[setIndex].Descriptors.Count; index++) - { - ResourceDescriptor descriptor = sets[setIndex].Descriptors[index]; - - if (currentDescriptor.Binding + currentCount != descriptor.Binding || - currentDescriptor.Type != descriptor.Type || - currentDescriptor.Stages != descriptor.Stages || - currentDescriptor.Count > 1 || - descriptor.Count > 1) - { - if (currentCount != 0) - { - currentSegments.Add(new ResourceBindingSegment( - currentDescriptor.Binding, - currentCount, - currentDescriptor.Type, - currentDescriptor.Stages, - currentDescriptor.Count > 1)); - } - - currentDescriptor = descriptor; - currentCount = descriptor.Count; - } - else - { - currentCount += descriptor.Count; - } - } - - if (currentCount != 0) - { - currentSegments.Add(new ResourceBindingSegment( - currentDescriptor.Binding, - currentCount, - currentDescriptor.Type, - currentDescriptor.Stages, - currentDescriptor.Count > 1)); - } - - segments[setIndex] = currentSegments.ToArray(); - } - - return segments; - } - private static (ResourceBindingSegment[][], int[], int[]) BuildBindingSegments(ReadOnlyCollection setUsages) { ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; -- 2.47.1 From d65053813845d84fa6c188578be26c4d464aefa3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jul 2024 11:32:45 +0100 Subject: [PATCH 308/368] Bind TextureBuffers --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 +- .../EncoderStateManager.cs | 144 ++++++++---------- 2 files changed, 65 insertions(+), 83 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 5ee8bd3d8..77e711fbb 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -54,10 +54,10 @@ namespace Ryujinx.Graphics.Metal record struct TextureRef { public ShaderStage Stage; - public Texture Storage; + public TextureBase Storage; public Sampler Sampler; - public TextureRef(ShaderStage stage, Texture storage, Sampler sampler) + public TextureRef(ShaderStage stage, TextureBase storage, Sampler sampler) { Stage = stage; Storage = storage; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index db7b81dce..4a7c8a00d 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -805,13 +805,9 @@ namespace Ryujinx.Graphics.Metal public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) { - if (texture is TextureBuffer) + if (texture != null) { - // TODO: Texture buffers - } - else if (texture is Texture view) - { - _currentState.TextureRefs[binding] = new(stage, view, sampler); + _currentState.TextureRefs[binding] = new(stage, texture, sampler); } else { @@ -1124,59 +1120,52 @@ namespace Ryujinx.Graphics.Metal case MetalRenderer.TextureSetIndex: if (!segment.IsArray) { - if (segment.Type != ResourceType.BufferTexture) + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) + int index = binding + i; + + ref var texture = ref _currentState.TextureRefs[index]; + + var storage = texture.Storage; + + if (storage == null) { - int index = binding + i; - - ref var texture = ref _currentState.TextureRefs[index]; - - var storage = texture.Storage; - - if (storage == null) - { - continue; - } - - var mtlTexture = storage.GetHandle(); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; - vertResourceIdIndex++; - - if (texture.Sampler != null) - { - vertResourceIds[vertResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; - vertResourceIdIndex++; - } - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; - fragResourceIdIndex++; - - if (texture.Sampler != null) - { - fragResourceIds[fragResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; - fragResourceIdIndex++; - } - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages); + continue; } - } - else - { - // TODO: Buffer textures + + var mtlTexture = storage.GetHandle(); + + MTLRenderStages renderStages = 0; + + if ((segment.Stages & ResourceStages.Vertex) != 0) + { + vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIdIndex++; + + if (texture.Sampler != null) + { + vertResourceIds[vertResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + vertResourceIdIndex++; + } + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if ((segment.Stages & ResourceStages.Fragment) != 0) + { + fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIdIndex++; + + if (texture.Sampler != null) + { + fragResourceIds[fragResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + fragResourceIdIndex++; + } + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages); } } else @@ -1343,41 +1332,34 @@ namespace Ryujinx.Graphics.Metal case MetalRenderer.TextureSetIndex: if (!segment.IsArray) { - if (segment.Type != ResourceType.BufferTexture) + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) + int index = binding + i; + + ref var texture = ref _currentState.TextureRefs[index]; + + var storage = texture.Storage; + + if (storage == null) { - int index = binding + i; + continue; + } - ref var texture = ref _currentState.TextureRefs[index]; + var mtlTexture = storage.GetHandle(); - var storage = texture.Storage; + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); + resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resourceIdIndex++; - if (storage == null) + if (texture.Sampler != null) { - continue; - } - - var mtlTexture = storage.GetHandle(); - - if (segment.Stages.HasFlag(ResourceStages.Compute)) - { - computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); - resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resourceIds[resourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; resourceIdIndex++; - - if (texture.Sampler != null) - { - resourceIds[resourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; - resourceIdIndex++; - } } } } - else - { - // TODO: Buffer textures - } } else { -- 2.47.1 From 0642df4909202bd35d7b1ba742c62654e8139645 Mon Sep 17 00:00:00 2001 From: Gabriel A Date: Sat, 27 Jul 2024 17:01:08 -0300 Subject: [PATCH 309/368] Start building more accurate vertex as compute usage info --- .../Shader/ShaderCache.cs | 4 +- .../Shader/ShaderInfoBuilder.cs | 24 +++- .../Translation/ResourceManager.cs | 103 ++++++++++++++++++ .../Translation/TranslatorContext.cs | 25 +++-- 4 files changed, 143 insertions(+), 13 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 64ea7c979..2ac705df6 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -435,6 +435,8 @@ namespace Ryujinx.Graphics.Gpu.Shader geometryAsCompute = CreateHostVertexAsComputeProgram(program, currentStage, tfEnabled); program = null; } + + infoBuilder.AddStageInfoVac(currentStage.GetVertexAsComputeInfo()); } if (program != null) @@ -533,7 +535,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled) { ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language); - ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, tfEnabled); + ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, context.GetVertexAsComputeInfo(), tfEnabled); return new(_context.Renderer.CreateProgram(new[] { source }, info), program.Info, context.GetResourceReservations()); } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 54a03f43b..24c698392 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false) { AddDescriptor(stages, type, setIndex, start, count); - AddUsage(stages, type, setIndex, start, count, write); + // AddUsage(stages, type, setIndex, start, count, write); } /// @@ -162,6 +162,25 @@ namespace Ryujinx.Graphics.Gpu.Shader AddUsage(info.Images, stages, isImage: true); } + public void AddStageInfoVac(ShaderProgramInfo info) + { + ResourceStages stages = info.Stage switch + { + ShaderStage.Compute => ResourceStages.Compute, + ShaderStage.Vertex => ResourceStages.Vertex, + ShaderStage.TessellationControl => ResourceStages.TessellationControl, + ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation, + ShaderStage.Geometry => ResourceStages.Geometry, + ShaderStage.Fragment => ResourceStages.Fragment, + _ => ResourceStages.None, + }; + + AddUsage(info.CBuffers, stages, isStorage: false); + AddUsage(info.SBuffers, stages, isStorage: true); + AddUsage(info.Textures, stages, isImage: false); + AddUsage(info.Images, stages, isImage: true); + } + /// /// Adds a resource descriptor to the list of descriptors. /// @@ -421,11 +440,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Indicates if the graphics shader is used with transform feedback enabled /// True if the compute shader comes from a disk cache, false otherwise /// Shader information - public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false) + public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, ShaderProgramInfo info2, bool tfEnabled, bool fromCache = false) { ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute); builder.AddStageInfo(info, vertexAsCompute: true); + builder.AddStageInfoVac(info2); return builder.Build(null, fromCache); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 94691a5b4..4f0fd4ceb 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -43,6 +43,11 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly Dictionary _usedTextures; private readonly Dictionary _usedImages; + private readonly List _vacConstantBuffers; + private readonly List _vacStorageBuffers; + private readonly List _vacTextures; + private readonly List _vacImages; + public int LocalMemoryId { get; private set; } public int SharedMemoryId { get; private set; } @@ -78,6 +83,11 @@ namespace Ryujinx.Graphics.Shader.Translation _usedTextures = new(); _usedImages = new(); + _vacConstantBuffers = new(); + _vacStorageBuffers = new(); + _vacTextures = new(); + _vacImages = new(); + Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, SupportBuffer.Binding, "support_buffer", SupportBuffer.GetStructureType())); LocalMemoryId = -1; @@ -563,6 +573,75 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors.ToArray(); } + public ShaderProgramInfo GetVertexAsComputeInfo() + { + var cbDescriptors = new BufferDescriptor[_vacConstantBuffers.Count]; + int cbDescriptorIndex = 0; + + foreach (BufferDefinition definition in _vacConstantBuffers) + { + cbDescriptors[cbDescriptorIndex++] = new BufferDescriptor(definition.Set, definition.Binding, 0, 0, 0, BufferUsageFlags.None); + } + + var sbDescriptors = new BufferDescriptor[_vacStorageBuffers.Count]; + int sbDescriptorIndex = 0; + + foreach (BufferDefinition definition in _vacStorageBuffers) + { + sbDescriptors[sbDescriptorIndex++] = new BufferDescriptor(definition.Set, definition.Binding, 0, 0, 0, BufferUsageFlags.Write); + } + + var tDescriptors = new TextureDescriptor[_vacTextures.Count]; + int tDescriptorIndex = 0; + + foreach (TextureDefinition definition in _vacTextures) + { + tDescriptors[tDescriptorIndex++] = new TextureDescriptor( + definition.Set, + definition.Binding, + definition.Type, + definition.Format, + 0, + 0, + definition.ArrayLength, + definition.Separate, + definition.Flags); + } + + var iDescriptors = new TextureDescriptor[_vacImages.Count]; + int iDescriptorIndex = 0; + + foreach (TextureDefinition definition in _vacImages) + { + iDescriptors[iDescriptorIndex++] = new TextureDescriptor( + definition.Set, + definition.Binding, + definition.Type, + definition.Format, + 0, + 0, + definition.ArrayLength, + definition.Separate, + definition.Flags); + } + + return new ShaderProgramInfo( + cbDescriptors, + sbDescriptors, + tDescriptors, + iDescriptors, + ShaderStage.Compute, + 0, + 0, + 0, + false, + false, + false, + false, + 0, + 0); + } + public bool TryGetCbufSlotAndHandleForTexture(int binding, out int cbufSlot, out int handle) { foreach ((TextureInfo info, TextureMeta meta) in _usedTextures) @@ -629,6 +708,30 @@ namespace Ryujinx.Graphics.Shader.Translation Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type)); } + public void AddVertexAsComputeConstantBuffer(BufferDefinition definition) + { + _vacConstantBuffers.Add(definition); + Properties.AddOrUpdateConstantBuffer(definition); + } + + public void AddVertexAsComputeStorageBuffer(BufferDefinition definition) + { + _vacStorageBuffers.Add(definition); + Properties.AddOrUpdateStorageBuffer(definition); + } + + public void AddVertexAsComputeTexture(TextureDefinition definition) + { + _vacTextures.Add(definition); + Properties.AddOrUpdateTexture(definition); + } + + public void AddVertexAsComputeImage(TextureDefinition definition) + { + _vacImages.Add(definition); + Properties.AddOrUpdateImage(definition); + } + public static string GetShaderStagePrefix(ShaderStage stage) { uint index = (uint)stage; diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 246867ba5..49116d8a3 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -394,7 +394,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int binding = resourceManager.Reservations.GetTfeBufferStorageBufferBinding(i); BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); - resourceManager.Properties.AddOrUpdateStorageBuffer(tfeDataBuffer); + resourceManager.AddVertexAsComputeStorageBuffer(tfeDataBuffer); } } @@ -402,7 +402,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding; BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType()); - resourceManager.Properties.AddOrUpdateConstantBuffer(vertexInfoBuffer); + resourceManager.AddVertexAsComputeConstantBuffer(vertexInfoBuffer); StructureType vertexOutputStruct = new(new StructureField[] { @@ -411,13 +411,13 @@ namespace Ryujinx.Graphics.Shader.Translation int vertexOutputSbBinding = resourceManager.Reservations.VertexOutputStorageBufferBinding; BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexOutputSbBinding, "vertex_output", vertexOutputStruct); - resourceManager.Properties.AddOrUpdateStorageBuffer(vertexOutputBuffer); + resourceManager.AddVertexAsComputeStorageBuffer(vertexOutputBuffer); if (Stage == ShaderStage.Vertex) { SetBindingPair ibSetAndBinding = resourceManager.Reservations.GetIndexBufferTextureSetAndBinding(); TextureDefinition indexBuffer = new(ibSetAndBinding.SetIndex, ibSetAndBinding.Binding, "ib_data", SamplerType.TextureBuffer); - resourceManager.Properties.AddOrUpdateTexture(indexBuffer); + resourceManager.AddVertexAsComputeTexture(indexBuffer); int inputMap = _program.AttributeUsage.UsedInputAttributes; @@ -426,7 +426,7 @@ namespace Ryujinx.Graphics.Shader.Translation int location = BitOperations.TrailingZeroCount(inputMap); SetBindingPair setAndBinding = resourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location); TextureDefinition vaBuffer = new(setAndBinding.SetIndex, setAndBinding.Binding, $"vb_data{location}", SamplerType.TextureBuffer); - resourceManager.Properties.AddOrUpdateTexture(vaBuffer); + resourceManager.AddVertexAsComputeTexture(vaBuffer); inputMap &= ~(1 << location); } @@ -435,11 +435,11 @@ namespace Ryujinx.Graphics.Shader.Translation { SetBindingPair trbSetAndBinding = resourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(); TextureDefinition remapBuffer = new(trbSetAndBinding.SetIndex, trbSetAndBinding.Binding, "trb_data", SamplerType.TextureBuffer); - resourceManager.Properties.AddOrUpdateTexture(remapBuffer); + resourceManager.AddVertexAsComputeTexture(remapBuffer); int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; BufferDefinition geometryVbOutputBuffer = new(BufferLayout.Std430, 1, geometryVbOutputSbBinding, "geometry_vb_output", vertexOutputStruct); - resourceManager.Properties.AddOrUpdateStorageBuffer(geometryVbOutputBuffer); + resourceManager.AddVertexAsComputeStorageBuffer(geometryVbOutputBuffer); StructureType geometryIbOutputStruct = new(new StructureField[] { @@ -448,7 +448,7 @@ namespace Ryujinx.Graphics.Shader.Translation int geometryIbOutputSbBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding; BufferDefinition geometryIbOutputBuffer = new(BufferLayout.Std430, 1, geometryIbOutputSbBinding, "geometry_ib_output", geometryIbOutputStruct); - resourceManager.Properties.AddOrUpdateStorageBuffer(geometryIbOutputBuffer); + resourceManager.AddVertexAsComputeStorageBuffer(geometryIbOutputBuffer); } resourceManager.SetVertexAsComputeLocalMemories(Definitions.Stage, Definitions.InputTopology); @@ -481,6 +481,11 @@ namespace Ryujinx.Graphics.Shader.Translation return new ResourceReservations(GpuAccessor, IsTransformFeedbackEmulated, vertexAsCompute: true, _vertexOutput, ioUsage); } + public ShaderProgramInfo GetVertexAsComputeInfo() + { + return CreateResourceManager(true).GetVertexAsComputeInfo(); + } + public void SetVertexOutputMapForGeometryAsCompute(TranslatorContext vertexContext) { _vertexOutput = vertexContext._program.GetIoUsage(); @@ -498,7 +503,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (Stage == ShaderStage.Vertex) { BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType()); - resourceManager.Properties.AddOrUpdateConstantBuffer(vertexInfoBuffer); + resourceManager.AddVertexAsComputeConstantBuffer(vertexInfoBuffer); } StructureType vertexInputStruct = new(new StructureField[] @@ -508,7 +513,7 @@ namespace Ryujinx.Graphics.Shader.Translation int vertexDataSbBinding = reservations.VertexOutputStorageBufferBinding; BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexDataSbBinding, "vb_input", vertexInputStruct); - resourceManager.Properties.AddOrUpdateStorageBuffer(vertexOutputBuffer); + resourceManager.AddVertexAsComputeStorageBuffer(vertexOutputBuffer); var context = new EmitterContext(); -- 2.47.1 From a84ed6af0d5482b823f54335ec1d8cc985e1234b Mon Sep 17 00:00:00 2001 From: Gabriel A Date: Sat, 27 Jul 2024 21:04:56 -0300 Subject: [PATCH 310/368] Fixes --- src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 5 ++--- src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs | 2 +- src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs | 4 ++-- .../Translation/TranslatorContext.cs | 6 +++--- src/Ryujinx.ShaderTools/Program.cs | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 2ac705df6..4473e8cb3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -428,15 +428,14 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage; - program = lastInVertexPipeline.GenerateVertexPassthroughForCompute(); + (program, ShaderProgramInfo vacInfo) = lastInVertexPipeline.GenerateVertexPassthroughForCompute(); + infoBuilder.AddStageInfoVac(vacInfo); } else { geometryAsCompute = CreateHostVertexAsComputeProgram(program, currentStage, tfEnabled); program = null; } - - infoBuilder.AddStageInfoVac(currentStage.GetVertexAsComputeInfo()); } if (program != null) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 24c698392..e283d0832 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -444,8 +444,8 @@ namespace Ryujinx.Graphics.Gpu.Shader { ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute); - builder.AddStageInfo(info, vertexAsCompute: true); builder.AddStageInfoVac(info2); + builder.AddStageInfo(info, vertexAsCompute: true); return builder.Build(null, fromCache); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 4f0fd4ceb..83e4dc0ac 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -573,7 +573,7 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors.ToArray(); } - public ShaderProgramInfo GetVertexAsComputeInfo() + public ShaderProgramInfo GetVertexAsComputeInfo(bool isVertex = false) { var cbDescriptors = new BufferDescriptor[_vacConstantBuffers.Count]; int cbDescriptorIndex = 0; @@ -630,7 +630,7 @@ namespace Ryujinx.Graphics.Shader.Translation sbDescriptors, tDescriptors, iDescriptors, - ShaderStage.Compute, + isVertex ? ShaderStage.Vertex : ShaderStage.Compute, 0, 0, 0, diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 49116d8a3..5ca17690e 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -491,7 +491,7 @@ namespace Ryujinx.Graphics.Shader.Translation _vertexOutput = vertexContext._program.GetIoUsage(); } - public ShaderProgram GenerateVertexPassthroughForCompute() + public (ShaderProgram, ShaderProgramInfo) GenerateVertexPassthroughForCompute() { var attributeUsage = new AttributeUsage(GpuAccessor); var resourceManager = new ResourceManager(ShaderStage.Vertex, GpuAccessor); @@ -571,14 +571,14 @@ namespace Ryujinx.Graphics.Shader.Translation LastInVertexPipeline = true }; - return Generate( + return (Generate( new[] { function }, attributeUsage, definitions, definitions, resourceManager, FeatureFlags.None, - 0); + 0), resourceManager.GetVertexAsComputeInfo(isVertex: true)); } public ShaderProgram GenerateGeometryPassthrough() diff --git a/src/Ryujinx.ShaderTools/Program.cs b/src/Ryujinx.ShaderTools/Program.cs index a84d7b466..564960c6f 100644 --- a/src/Ryujinx.ShaderTools/Program.cs +++ b/src/Ryujinx.ShaderTools/Program.cs @@ -116,7 +116,7 @@ namespace Ryujinx.ShaderTools if (options.VertexPassthrough) { - program = translatorContext.GenerateVertexPassthroughForCompute(); + (program, _) = translatorContext.GenerateVertexPassthroughForCompute(); } else { -- 2.47.1 From 45c99dbfa8a4d4eaf90aff51cbc98d783b3e72d3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 28 Jul 2024 18:45:43 +0100 Subject: [PATCH 311/368] Partial indirect draw support --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 54 ++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 2d70903cf..617034cf8 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -402,30 +402,68 @@ namespace Ryujinx.Graphics.Metal public void DrawIndexedIndirect(BufferRange indirectBuffer) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(true); + // TODO: Reindex unsupported topologies + if (TopologyUnsupported(_encoderStateManager.Topology)) + { + Logger.Warning?.Print(LogClass.Gpu, $"Drawing indexed with unsupported topology: {_encoderStateManager.Topology}"); + } - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + var buffer = _renderer.BufferManager + .GetBuffer(indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + + var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); + + (MTLBuffer indexBuffer, int offset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); + + if (indexBuffer.NativePtr != IntPtr.Zero && buffer.NativePtr != IntPtr.Zero) + { + var renderCommandEncoder = GetOrCreateRenderEncoder(true); + + renderCommandEncoder.DrawIndexedPrimitives( + primitiveType, + type, + indexBuffer, + (ulong)offset, + buffer, + (ulong)indirectBuffer.Offset); + } } public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(true); + // TODO: Properly support count - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + DrawIndexedIndirect(indirectBuffer); } public void DrawIndirect(BufferRange indirectBuffer) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(true); + var renderCommandEncoder = GetOrCreateRenderEncoder(true); - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (TopologyUnsupported(_encoderStateManager.Topology)) + { + // TODO: Reindex unsupported topologies + Logger.Warning?.Print(LogClass.Gpu, $"Drawing indirect with unsupported topology: {_encoderStateManager.Topology}"); + } + + var buffer = _renderer.BufferManager + .GetBuffer(indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + + var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); + + renderCommandEncoder.DrawPrimitives( + primitiveType, + buffer, + (ulong)indirectBuffer.Offset); } public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - // var renderCommandEncoder = GetOrCreateRenderEncoder(true); + // TODO: Properly support count - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + DrawIndirect(indirectBuffer); } public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) -- 2.47.1 From 421ba5448aa63c68fb02b53d8a073a76fd42319a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 28 Jul 2024 18:46:58 +0100 Subject: [PATCH 312/368] Properly register TextureBuffer usage + Store Auto ref --- .../EncoderStateManager.cs | 10 ++++++++++ src/Ryujinx.Graphics.Metal/TextureBuffer.cs | 16 ++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 4a7c8a00d..f515293b8 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -1133,6 +1133,11 @@ namespace Ryujinx.Graphics.Metal continue; } + if (storage is TextureBuffer textureBuffer) + { + textureBuffer.RebuildStorage(false); + } + var mtlTexture = storage.GetHandle(); MTLRenderStages renderStages = 0; @@ -1345,6 +1350,11 @@ namespace Ryujinx.Graphics.Metal continue; } + if (storage is TextureBuffer textureBuffer) + { + textureBuffer.RebuildStorage(false); + } + var mtlTexture = storage.GetHandle(); if (segment.Stages.HasFlag(ResourceStages.Compute)) diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs index 388b77e23..8d23bad98 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs @@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Metal private int _size; private int _bufferCount; + private Auto _buffer; public TextureBuffer(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) { @@ -32,25 +33,20 @@ namespace Ryujinx.Graphics.Metal MtlFormat = pixelFormat; } - private void RebuildStorage() + public void RebuildStorage(bool write) { - // Find the parent buffer, and try to build a texture from it. - - // TODO: texture uses should register read/write usage on the assigned buffer. - Auto bufferAuto = Renderer.BufferManager.GetBuffer(_bufferHandle, false); - - if (MtlTexture.NativePtr != 0) + if (MtlTexture != IntPtr.Zero) { MtlTexture.Dispose(); } - if (bufferAuto == null) + if (_buffer == null) { MtlTexture = default; } else { - DisposableBuffer buffer = bufferAuto.Get(Pipeline.Cbs, _offset, _size); + DisposableBuffer buffer = _buffer.Get(Pipeline.Cbs, _offset, _size, write); _descriptor.Width = (uint)(_size / Info.BytesPerPixel); MtlTexture = buffer.Value.NewTexture(_descriptor, (ulong)_offset, (ulong)_size); @@ -123,7 +119,7 @@ namespace Ryujinx.Graphics.Metal _size = buffer.Size; _bufferCount = Renderer.BufferManager.BufferCount; - RebuildStorage(); + _buffer = Renderer.BufferManager.GetBuffer(_bufferHandle, false); } public override void Release() -- 2.47.1 From a0c67cab44ea9e74cb29aa3903d6fac7e30611f0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 28 Jul 2024 18:48:17 +0100 Subject: [PATCH 313/368] Remove DummyBufferTextures Mostly gets VTG on Compute working again --- .../Threed/ComputeDraw/VtgAsComputeContext.cs | 15 --------------- .../Threed/ComputeDraw/VtgAsComputeState.cs | 18 ------------------ 2 files changed, 33 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs index 6dba27a7d..a7840fb03 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs @@ -521,21 +521,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write); } - /// - /// Gets the range for a dummy 16 bytes buffer, filled with zeros. - /// - /// Dummy buffer range - public BufferRange GetDummyBufferRange() - { - if (_dummyBuffer == BufferHandle.Null) - { - _dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory); - _context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0); - } - - return new BufferRange(_dummyBuffer, 0, DummyBufferSize); - } - /// /// Gets the range for a sequential index buffer, with ever incrementing index values. /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 73682866b..2de324392 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -147,7 +147,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); - SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format); continue; } @@ -163,15 +162,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); - SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format); continue; } int vbStride = vertexBuffer.UnpackStride(); ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count); - ulong oldVbSize = vbSize; - ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset(); int componentSize = format.GetScalarSize(); @@ -345,20 +341,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw return maxOutputVertices / verticesPerPrimitive; } - /// - /// Binds a dummy buffer as vertex buffer into a buffer texture. - /// - /// Shader resource binding reservations - /// Buffer texture index - /// Buffer texture format - private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format) - { - ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format); - bufferTexture.SetStorage(_vacContext.GetDummyBufferRange()); - - _context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null); - } - /// /// Binds a vertex buffer into a buffer texture. /// -- 2.47.1 From 8bbfa86538562674877a4995839dc3c97e52f84b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 28 Jul 2024 22:52:31 +0100 Subject: [PATCH 314/368] Get Tomb Raider working --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index f7f5eed46..6e710f2ce 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -66,6 +66,7 @@ namespace Ryujinx.Graphics.Metal Add(Format.S8Uint, MTLPixelFormat.Stencil8); Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm); Add(Format.S8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8); + Add(Format.X8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8); Add(Format.D32Float, MTLPixelFormat.Depth32Float); Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24UnormStencil8); Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8); -- 2.47.1 From c0dcb6c4f249c94fbfe1583661f54bf63c81b8b0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 29 Jul 2024 00:51:53 +0100 Subject: [PATCH 315/368] Dual Source Blend Support in Shader Fixes Super Mario Galaxy and The Legend of Zelda: Skyward Sword HD --- .../CodeGen/Msl/Declarations.cs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 346beb02e..e31e397c1 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; @@ -308,7 +309,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { if (context.Definitions.IaIndexing) { - // Not handled + Logger.Warning?.PrintMsg(LogClass.Gpu, "Unhandled IA Indexing!"); } else { @@ -390,9 +391,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable outputs) { - if (context.Definitions.IaIndexing) + if (context.Definitions.OaIndexing) { - // Not handled + Logger.Warning?.PrintMsg(LogClass.Gpu, "Unhandled OA Indexing!"); } else { @@ -415,7 +416,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.EnterScope(); - foreach (var ioDefinition in outputs.OrderBy(x => x.Location)) + outputs = outputs.OrderBy(x => x.Location); + + if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) + { + IoDefinition firstOutput = outputs.ElementAtOrDefault(0); + IoDefinition secondOutput = outputs.ElementAtOrDefault(1); + + var type1 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(firstOutput.Location)); + var type2 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(secondOutput.Location)); + + var name1 = $"color{firstOutput.Location}"; + var name2 = $"color{firstOutput.Location + 1}"; + + context.AppendLine($"{type1} {name1} [[color({firstOutput.Location}), index(0)]];"); + context.AppendLine($"{type2} {name2} [[color({firstOutput.Location}), index(1)]];"); + + outputs = outputs.Skip(2); + } + + foreach (var ioDefinition in outputs) { string type = ioDefinition.IoVariable switch { -- 2.47.1 From 04bd1fa1caebf57f4e4dff9fed272497c4c6f76e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 29 Jul 2024 01:02:59 +0100 Subject: [PATCH 316/368] Use RGBA8Unorm for R4G4B4A4Unorm Gets SM64 to boot --- src/Ryujinx.Graphics.Metal/FormatTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 6e710f2ce..784ba183a 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.Metal Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8); Add(Format.R8G8B8A8Srgb, MTLPixelFormat.RGBA8UnormsRGB); // Add(Format.R4G4Unorm, MTLPixelFormat.R4G4Unorm); - // Add(Format.R4G4B4A4Unorm, MTLPixelFormat.R4G4B4A4Unorm); + Add(Format.R4G4B4A4Unorm, MTLPixelFormat.RGBA8Unorm); // Add(Format.R5G5B5X1Unorm, MTLPixelFormat.R5G5B5X1Unorm); Add(Format.R5G5B5A1Unorm, MTLPixelFormat.BGR5A1Unorm); Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm); -- 2.47.1 From 8f6eceaa1feac9a4369a80a49e4f63687366580d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 29 Jul 2024 01:25:27 +0100 Subject: [PATCH 317/368] Fix hex number type ambiguity Fixes cutscenes in Super Mario Sunshine --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs index 63ecbc0aa..8d288da3e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs @@ -76,7 +76,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return value.ToString(CultureInfo.InvariantCulture); } - return "0x" + value.ToString("X", CultureInfo.InvariantCulture); + return $"as_type(0x{value.ToString("X", CultureInfo.InvariantCulture)})"; } public static string FormatUint(uint value) @@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return value.ToString(CultureInfo.InvariantCulture) + "u"; } - return "0x" + value.ToString("X", CultureInfo.InvariantCulture) + "u"; + return $"as_type(0x{value.ToString("X", CultureInfo.InvariantCulture)})"; } } } -- 2.47.1 From 9d3fc82484bba6034ce7b9ef20b6a7a4b46fa484 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 31 Jul 2024 16:29:48 +0100 Subject: [PATCH 318/368] TextureArray & ImageArray Creation + State --- src/Ryujinx.Graphics.Metal/EncoderState.cs | 9 ++ .../EncoderStateManager.cs | 72 ++++++++++++++++ src/Ryujinx.Graphics.Metal/ImageArray.cs | 74 +++++++++++++++++ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 5 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 34 +++++++- src/Ryujinx.Graphics.Metal/TextureArray.cs | 83 +++++++++++++++++++ 6 files changed, 270 insertions(+), 7 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/ImageArray.cs create mode 100644 src/Ryujinx.Graphics.Metal/TextureArray.cs diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 77e711fbb..8d408d847 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -56,6 +56,7 @@ namespace Ryujinx.Graphics.Metal public ShaderStage Stage; public TextureBase Storage; public Sampler Sampler; + public Format ImageFormat; public TextureRef(ShaderStage stage, TextureBase storage, Sampler sampler) { @@ -101,11 +102,19 @@ namespace Ryujinx.Graphics.Metal public PipelineState Pipeline; public DepthStencilUid DepthStencilUid; + public readonly record struct ArrayRef(ShaderStage Stage, T Array); + public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings]; public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxTextureBindings]; + public ArrayRef[] TextureArrayRefs = []; + public ArrayRef[] ImageArrayRefs = []; + + public ArrayRef[] TextureArrayExtraRefs = []; + public ArrayRef[] ImageArrayExtraRefs = []; + public IndexBufferState IndexBuffer = default; public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index f515293b8..0f98496bc 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -14,6 +14,8 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] struct EncoderStateManager : IDisposable { + private const int ArrayGrowthSize = 16; + private readonly MTLDevice _device; private readonly Pipeline _pipeline; private readonly BufferManager _bufferManager; @@ -90,6 +92,16 @@ namespace Ryujinx.Graphics.Metal _currentState.ClearLoadAction = clear; } + public void DirtyTextures() + { + _currentState.Dirty |= DirtyFlags.Textures; + } + + public void DirtyImages() + { + _currentState.Dirty |= DirtyFlags.Images; + } + public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() { // Initialise Pass & State @@ -831,6 +843,66 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.Images; } + public void UpdateTextureArray(ShaderStage stage, ulong binding, TextureArray array) + { + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, (int)binding, ArrayGrowthSize); + + if (arrayRef.Stage != stage || arrayRef.Array != array) + { + arrayRef = new EncoderState.ArrayRef(stage, array); + + _currentState.Dirty |= DirtyFlags.Textures; + } + } + + public void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array) + { + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, setIndex); + + if (arrayRef.Stage != stage || arrayRef.Array != array) + { + arrayRef = new EncoderState.ArrayRef(stage, array); + + _currentState.Dirty |= DirtyFlags.Textures; + } + } + + public void UpdateImageArray(ShaderStage stage, ulong binding, ImageArray array) + { + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayRefs, (int)binding, ArrayGrowthSize); + + if (arrayRef.Stage != stage || arrayRef.Array != array) + { + arrayRef = new EncoderState.ArrayRef(stage, array); + + _currentState.Dirty |= DirtyFlags.Images; + } + } + + public void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array) + { + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex); + + if (arrayRef.Stage != stage || arrayRef.Array != array) + { + arrayRef = new EncoderState.ArrayRef(stage, array); + + _currentState.Dirty |= DirtyFlags.Images; + } + } + + private static ref EncoderState.ArrayRef GetArrayRef(ref EncoderState.ArrayRef[] array, int index, int growthSize = 1) + { + ArgumentOutOfRangeException.ThrowIfNegative(index); + + if (array.Length <= index) + { + Array.Resize(ref array, index + growthSize); + } + + return ref array[index]; + } + private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); diff --git a/src/Ryujinx.Graphics.Metal/ImageArray.cs b/src/Ryujinx.Graphics.Metal/ImageArray.cs new file mode 100644 index 000000000..67b8186b1 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/ImageArray.cs @@ -0,0 +1,74 @@ +using Ryujinx.Graphics.GAL; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal class ImageArray : IImageArray + { + private readonly TextureRef[] _textureRefs; + private readonly TextureBuffer[] _bufferTextureRefs; + + private readonly bool _isBuffer; + private readonly Pipeline _pipeline; + + public ImageArray(int size, bool isBuffer, Pipeline pipeline) + { + if (isBuffer) + { + _bufferTextureRefs = new TextureBuffer[size]; + } + else + { + _textureRefs = new TextureRef[size]; + } + + _isBuffer = isBuffer; + _pipeline = pipeline; + } + + public void SetFormats(int index, Format[] imageFormats) + { + for (int i = 0; i < imageFormats.Length; i++) + { + _textureRefs[index + i].ImageFormat = imageFormats[i]; + } + + SetDirty(); + } + + public void SetImages(int index, ITexture[] images) + { + for (int i = 0; i < images.Length; i++) + { + ITexture image = images[i]; + + if (image is TextureBuffer textureBuffer) + { + _bufferTextureRefs[index + i] = textureBuffer; + } + else if (image is Texture texture) + { + _textureRefs[index + i].Storage = texture; + } + else if (!_isBuffer) + { + _textureRefs[index + i].Storage = null; + } + else + { + _bufferTextureRefs[index + i] = null; + } + } + + SetDirty(); + } + + private void SetDirty() + { + _pipeline.DirtyImages(); + } + + public void Dispose() { } + } +} diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index fa24e70a0..cd2a83bff 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Metal; @@ -97,7 +96,7 @@ namespace Ryujinx.Graphics.Metal public IImageArray CreateImageArray(int size, bool isBuffer) { - throw new NotImplementedException(); + return new ImageArray(size, isBuffer, _pipeline); } public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) @@ -122,7 +121,7 @@ namespace Ryujinx.Graphics.Metal public ITextureArray CreateTextureArray(int size, bool isBuffer) { - throw new NotImplementedException(); + return new TextureArray(size, isBuffer, _pipeline); } public bool PrepareHostMapping(IntPtr address, ulong size) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 617034cf8..39f85d205 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -192,6 +192,16 @@ namespace Ryujinx.Graphics.Metal _renderer.RegisterFlush(); } + public void DirtyTextures() + { + _encoderStateManager.DirtyTextures(); + } + + public void DirtyImages() + { + _encoderStateManager.DirtyImages(); + } + public void Blit( Texture src, Texture dst, @@ -542,12 +552,20 @@ namespace Ryujinx.Graphics.Metal public void SetImageArray(ShaderStage stage, int binding, IImageArray array) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (array is ImageArray imageArray) + { + var index = (ulong)binding; + + _encoderStateManager.UpdateImageArray(stage, index, imageArray); + } } public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (array is ImageArray imageArray) + { + _encoderStateManager.UpdateImageArraySeparate(stage, setIndex, imageArray); + } } public void SetLineParameters(float width, bool smooth) @@ -656,12 +674,20 @@ namespace Ryujinx.Graphics.Metal public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (array is TextureArray textureArray) + { + var index = (ulong)binding; + + _encoderStateManager.UpdateTextureArray(stage, index, textureArray); + } } public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (array is TextureArray textureArray) + { + _encoderStateManager.UpdateTextureArraySeparate(stage, setIndex, textureArray); + } } public void SetUserClipDistance(int index, bool enableClip) diff --git a/src/Ryujinx.Graphics.Metal/TextureArray.cs b/src/Ryujinx.Graphics.Metal/TextureArray.cs new file mode 100644 index 000000000..762e6a5fd --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/TextureArray.cs @@ -0,0 +1,83 @@ +using Ryujinx.Graphics.GAL; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal class TextureArray : ITextureArray + { + private readonly TextureRef[] _textureRefs; + private readonly TextureBuffer[] _bufferTextureRefs; + + private readonly bool _isBuffer; + private readonly Pipeline _pipeline; + + public TextureArray(int size, bool isBuffer, Pipeline pipeline) + { + if (isBuffer) + { + _bufferTextureRefs = new TextureBuffer[size]; + } + else + { + _textureRefs = new TextureRef[size]; + } + + _isBuffer = isBuffer; + _pipeline = pipeline; + } + + public void SetSamplers(int index, ISampler[] samplers) + { + for (int i = 0; i < samplers.Length; i++) + { + ISampler sampler = samplers[i]; + + if (sampler is Sampler samp) + { + _textureRefs[index + i].Sampler = samp; + } + else + { + _textureRefs[index + i].Sampler = default; + } + } + + SetDirty(); + } + + public void SetTextures(int index, ITexture[] textures) + { + for (int i = 0; i < textures.Length; i++) + { + ITexture texture = textures[i]; + + if (texture is TextureBuffer textureBuffer) + { + _bufferTextureRefs[index + i] = textureBuffer; + } + else if (texture is Texture tex) + { + _textureRefs[index + i].Storage = tex; + } + else if (!_isBuffer) + { + _textureRefs[index + i].Storage = null; + } + else + { + _bufferTextureRefs[index + i] = null; + } + } + + SetDirty(); + } + + private void SetDirty() + { + _pipeline.DirtyTextures(); + } + + public void Dispose() { } + } +} -- 2.47.1 From 5b88ea66ba5b506d0f5f2ef30be8ab8e748da96d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 31 Jul 2024 17:09:24 +0100 Subject: [PATCH 319/368] InstGenMemory Refactor + Bindless Support --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 140 +++++++++++------- 1 file changed, 87 insertions(+), 53 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index d13300e05..198d0cf8d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -159,8 +159,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions var texCallBuilder = new StringBuilder(); - string imageName = GetImageName(context.Properties, texOp); - texCallBuilder.Append($"images.{imageName}"); + int srcIndex = 0; + + string Src(AggregateType type) + { + return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); + } + + string imageName = GetImageName(context, texOp, ref srcIndex); + texCallBuilder.Append(imageName); texCallBuilder.Append('.'); if (texOp.Inst == Instruction.ImageAtomic) @@ -185,13 +192,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCallBuilder.Append(texOp.Inst == Instruction.ImageLoad ? "read" : "write"); } - int srcIndex = 0; - - string Src(AggregateType type) - { - return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); - } - texCallBuilder.Append('('); var coordsBuilder = new StringBuilder(); @@ -286,7 +286,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions int coordsCount = texOp.Type.GetDimensions(); int coordsIndex = 0; - string samplerName = GetSamplerName(context.Properties, texOp); + string textureName = GetTextureName(context, texOp, ref coordsIndex); + string samplerName = GetSamplerName(context, texOp, ref coordsIndex); string coordsExpr; @@ -306,8 +307,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions coordsExpr = GetSourceExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32); } - var clamped = $"textures.tex_{samplerName}.calculate_clamped_lod(textures.samp_{samplerName}, {coordsExpr})"; - var unclamped = $"textures.tex_{samplerName}.calculate_unclamped_lod(textures.samp_{samplerName}, {coordsExpr})"; + var clamped = $"{textureName}.calculate_clamped_lod({samplerName}, {coordsExpr})"; + var unclamped = $"{textureName}.calculate_unclamped_lod({samplerName}, {coordsExpr})"; return $"float2({clamped}, {unclamped}){GetMask(texOp.Index)}"; } @@ -332,11 +333,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; - bool colorIsVector = isGather || !isShadow; + var texCallBuilder = new StringBuilder(); - string samplerName = GetSamplerName(context.Properties, texOp); - string texCall = $"textures.tex_{samplerName}"; - texCall += "."; + bool colorIsVector = isGather || !isShadow; int srcIndex = 0; @@ -345,27 +344,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); } + string textureName = GetTextureName(context, texOp, ref srcIndex); + string samplerName = GetSamplerName(context, texOp, ref srcIndex); + + texCallBuilder.Append(textureName); + texCallBuilder.Append('.'); + if (intCoords) { - texCall += "read("; + texCallBuilder.Append("read("); } else { if (isGather) { - texCall += "gather"; + texCallBuilder.Append("gather"); } else { - texCall += "sample"; + texCallBuilder.Append("sample"); } if (isShadow) { - texCall += "_compare"; + texCallBuilder.Append("_compare"); } - texCall += $"(textures.samp_{samplerName}, "; + texCallBuilder.Append($"({samplerName}, "); } int coordsCount = texOp.Type.GetDimensions(); @@ -377,13 +382,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { if (appended) { - texCall += ", "; + texCallBuilder.Append(", "); } else { appended = true; } - texCall += str; + + texCallBuilder.Append(str); } AggregateType coordType = intCoords ? AggregateType.S32 : AggregateType.FP32; @@ -478,19 +484,52 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } } - texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); + texCallBuilder.Append(')'); + texCallBuilder.Append(colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); - return texCall; + return texCallBuilder.ToString(); } - private static string GetSamplerName(ShaderProperties resourceDefinitions, AstTextureOperation texOp) + private static string GetTextureName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) { - return resourceDefinitions.Textures[texOp.GetTextureSetAndBinding()].Name; + TextureDefinition textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()]; + string name = textureDefinition.Name; + + if (textureDefinition.ArrayLength != 1) + { + name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; + } + + return $"textures.tex_{name}"; } - private static string GetImageName(ShaderProperties resourceDefinitions, AstTextureOperation texOp) + private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) { - return resourceDefinitions.Images[texOp.GetTextureSetAndBinding()].Name; + var index = texOp.IsSeparate ? texOp.GetSamplerSetAndBinding() : texOp.GetTextureSetAndBinding(); + var sourceIndex = texOp.IsSeparate ? srcIndex++ : srcIndex + 1; + + TextureDefinition samplerDefinition = context.Properties.Textures[index]; + string name = samplerDefinition.Name; + + if (samplerDefinition.ArrayLength != 1) + { + name = $"{name}[{GetSourceExpr(context, texOp.GetSource(sourceIndex), AggregateType.S32)}]"; + } + + return $"textures.samp_{name}"; + } + + private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) + { + TextureDefinition definition = context.Properties.Images[texOp.GetTextureSetAndBinding()]; + string name = definition.Name; + + if (definition.ArrayLength != 1) + { + name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; + } + + return $"images.{name}"; } private static string GetMaskMultiDest(int mask) @@ -517,67 +556,62 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { AstTextureOperation texOp = (AstTextureOperation)operation; - bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + int srcIndex = 0; - // TODO: Bindless texture support. For now we just return 0. - if (isBindless) - { - return NumberFormatter.FormatInt(0); - } + string textureName = GetTextureName(context, texOp, ref srcIndex); - string samplerName = GetSamplerName(context.Properties, texOp); - string textureName = $"textures.tex_{samplerName}"; - string texCall = textureName + "."; - texCall += "get_num_samples()"; - - return texCall; + return $"{textureName}.get_num_samples()"; } public static string TextureQuerySize(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; - string samplerName = GetSamplerName(context.Properties, texOp); - string textureName = $"textures.tex_{samplerName}"; - string texCall = textureName + "."; + var texCallBuilder = new StringBuilder(); + + int srcIndex = 0; + + string textureName = GetTextureName(context, texOp, ref srcIndex); + texCallBuilder.Append(textureName); + texCallBuilder.Append('.'); if (texOp.Index == 3) { - texCall += "get_num_mip_levels()"; + texCallBuilder.Append("get_num_mip_levels()"); } else { context.Properties.Textures.TryGetValue(texOp.GetTextureSetAndBinding(), out TextureDefinition definition); bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; - texCall += "get_"; + texCallBuilder.Append("get_"); if (texOp.Index == 0) { - texCall += "width"; + texCallBuilder.Append("width"); } else if (texOp.Index == 1) { - texCall += "height"; + texCallBuilder.Append("height"); } else { - texCall += "depth"; + texCallBuilder.Append("depth"); } - texCall += "("; + texCallBuilder.Append('('); if (hasLod) { IAstNode lod = operation.GetSource(0); string lodExpr = GetSourceExpr(context, lod, GetSrcVarType(operation.Inst, 0)); - texCall += $"{lodExpr}"; + texCallBuilder.Append(lodExpr); } - texCall += ")"; + texCallBuilder.Append(')'); } - return texCall; + return texCallBuilder.ToString(); } public static string PackHalf2x16(CodeGenContext context, AstOperation operation) -- 2.47.1 From 80bb95dfb92faf5ff02933a84ac0c8a07da89f99 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Wed, 31 Jul 2024 23:32:37 +0100 Subject: [PATCH 320/368] Shader Extra Set Support + Cleanup (#36) Separate samplers are now supported and arrays in constant sets are bound --- src/Ryujinx.Graphics.Metal/Constants.cs | 23 +- .../EncoderStateManager.cs | 313 +++++++++++++----- src/Ryujinx.Graphics.Metal/HelperShader.cs | 11 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 20 +- .../ResourceLayoutBuilder.cs | 10 +- src/Ryujinx.Graphics.Metal/Shaders/Blit.metal | 4 +- .../Shaders/BlitMs.metal | 2 +- .../Shaders/ChangeBufferStride.metal | 4 +- .../Shaders/ColorClear.metal | 2 +- .../Shaders/DepthBlit.metal | 2 +- .../Shaders/DepthBlitMs.metal | 2 +- .../Shaders/DepthStencilClear.metal | 2 +- .../Shaders/StencilBlit.metal | 2 +- .../Shaders/StencilBlitMs.metal | 2 +- src/Ryujinx.Graphics.Metal/TextureArray.cs | 10 + .../CodeGen/Msl/Declarations.cs | 128 ++++--- .../CodeGen/Msl/Defaults.cs | 20 +- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 15 +- .../CodeGen/Msl/MslGenerator.cs | 19 +- src/Ryujinx.Graphics.Shader/SamplerType.cs | 2 +- 20 files changed, 412 insertions(+), 181 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 58735824d..133925e2d 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -2,7 +2,6 @@ namespace Ryujinx.Graphics.Metal { static class Constants { - // TODO: Check these values, these were largely copied from Vulkan public const int MaxShaderStages = 5; public const int MaxVertexBuffers = 16; public const int MaxUniformBuffersPerStage = 18; @@ -15,17 +14,25 @@ namespace Ryujinx.Graphics.Metal public const int MaxViewports = 16; // TODO: Check this value public const int MaxVertexAttributes = 31; - // TODO: Check this value - public const int MaxVertexLayouts = 31; public const int MinResourceAlignment = 16; // Must match constants set in shader generation - public const uint ZeroBufferIndex = 18; + public const uint ZeroBufferIndex = MaxVertexBuffers; + public const uint BaseSetIndex = MaxVertexBuffers + 1; - public const uint ConstantBuffersIndex = 20; - public const uint StorageBuffersIndex = 21; - public const uint TexturesIndex = 22; - public const uint ImagesIndex = 23; + public const uint ConstantBuffersIndex = BaseSetIndex; + public const uint StorageBuffersIndex = BaseSetIndex + 1; + public const uint TexturesIndex = BaseSetIndex + 2; + public const uint ImagesIndex = BaseSetIndex + 3; + + public const uint ConstantBuffersSetIndex = 0; + public const uint StorageBuffersSetIndex = 1; + public const uint TexturesSetIndex = 2; + public const uint ImagesSetIndex = 3; + + public const uint MaximumBufferArgumentTableEntries = 31; + + public const uint MaximumExtraSets = MaximumBufferArgumentTableEntries - ImagesIndex; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 0f98496bc..41a4140fd 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -57,11 +57,16 @@ namespace Ryujinx.Graphics.Metal _depthStencilCache.Dispose(); } + private readonly void SignalDirty(DirtyFlags flags) + { + _currentState.Dirty |= flags; + } + public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All) { _currentState = state ?? _mainState; - _currentState.Dirty |= flags; + SignalDirty(flags); return _mainState; } @@ -84,7 +89,7 @@ namespace Ryujinx.Graphics.Metal _currentState.Topology = state.Topology; _currentState.Viewports = state.Viewports; - _currentState.Dirty |= DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports; + SignalDirty(DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports); } public readonly void SetClearLoadAction(bool clear) @@ -94,12 +99,12 @@ namespace Ryujinx.Graphics.Metal public void DirtyTextures() { - _currentState.Dirty |= DirtyFlags.Textures; + SignalDirty(DirtyFlags.Textures); } public void DirtyImages() { - _currentState.Dirty |= DirtyFlags.Images; + SignalDirty(DirtyFlags.Images); } public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() @@ -161,7 +166,7 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); // Mark all state as dirty to ensure it is set on the encoder - _currentState.Dirty |= DirtyFlags.RenderAll; + SignalDirty(DirtyFlags.RenderAll); // Cleanup renderPassDescriptor.Dispose(); @@ -175,7 +180,7 @@ namespace Ryujinx.Graphics.Metal var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); // Mark all state as dirty to ensure it is set on the encoder - _currentState.Dirty |= DirtyFlags.ComputeAll; + SignalDirty(DirtyFlags.ComputeAll); // Cleanup descriptor.Dispose(); @@ -233,22 +238,22 @@ namespace Ryujinx.Graphics.Metal if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) { - UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.UniformSetIndex); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.ConstantBuffersSetIndex); } if ((_currentState.Dirty & DirtyFlags.Storages) != 0) { - UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.StorageSetIndex); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.StorageBuffersSetIndex); } if ((_currentState.Dirty & DirtyFlags.Textures) != 0) { - UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.TextureSetIndex); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.TexturesSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) + if ((_currentState.Dirty & DirtyFlags.Images) != 0) { - UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.ImageSetIndex); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.ImagesSetIndex); } _currentState.Dirty &= ~DirtyFlags.RenderAll; @@ -256,29 +261,29 @@ namespace Ryujinx.Graphics.Metal public readonly void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) { - if (_currentState.Dirty.HasFlag(DirtyFlags.ComputePipeline)) + if ((_currentState.Dirty & DirtyFlags.ComputePipeline) != 0) { SetComputePipelineState(computeCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms)) + if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) { - UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.UniformSetIndex); + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.ConstantBuffersSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Storages)) + if ((_currentState.Dirty & DirtyFlags.Storages) != 0) { - UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.StorageSetIndex); + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.StorageBuffersSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Textures)) + if ((_currentState.Dirty & DirtyFlags.Textures) != 0) { - UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.TextureSetIndex); + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.TexturesSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) + if ((_currentState.Dirty & DirtyFlags.Images) != 0) { - UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.ImageSetIndex); + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.ImagesSetIndex); } _currentState.Dirty &= ~DirtyFlags.ComputeAll; @@ -347,13 +352,13 @@ namespace Ryujinx.Graphics.Metal { _currentState.RenderProgram = prg; - _currentState.Dirty |= DirtyFlags.RenderPipeline | DirtyFlags.ArgBuffers; + SignalDirty(DirtyFlags.RenderPipeline | DirtyFlags.ArgBuffers); } else if (prg.ComputeFunction != IntPtr.Zero) { _currentState.ComputeProgram = prg; - _currentState.Dirty |= DirtyFlags.ComputePipeline | DirtyFlags.ArgBuffers; + SignalDirty(DirtyFlags.ComputePipeline | DirtyFlags.ArgBuffers); } } @@ -516,8 +521,7 @@ namespace Ryujinx.Graphics.Metal // Update the buffers on the pipeline UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); - // Mark dirty - _currentState.Dirty |= DirtyFlags.RenderPipeline; + SignalDirty(DirtyFlags.RenderPipeline); } public readonly void UpdateBlendDescriptors(int index, BlendDescriptor blend) @@ -541,11 +545,9 @@ namespace Ryujinx.Graphics.Metal _currentState.BlendColor = blend.BlendConstant; - // Mark dirty - _currentState.Dirty |= DirtyFlags.RenderPipeline; + SignalDirty(DirtyFlags.RenderPipeline); } - // Inlineable public void UpdateStencilState(StencilTestDescriptor stencilTest) { ref DepthStencilUid uid = ref _currentState.DepthStencilUid; @@ -574,8 +576,7 @@ namespace Ryujinx.Graphics.Metal UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); - // Mark dirty - _currentState.Dirty |= DirtyFlags.DepthStencil; + SignalDirty(DirtyFlags.DepthStencil); } public readonly void UpdateDepthState(DepthTestDescriptor depthTest) @@ -585,11 +586,9 @@ namespace Ryujinx.Graphics.Metal uid.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; uid.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable; - // Mark dirty - _currentState.Dirty |= DirtyFlags.DepthStencil; + SignalDirty(DirtyFlags.DepthStencil); } - // Inlineable public readonly void UpdateDepthClamp(bool clamp) { _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; @@ -601,11 +600,9 @@ namespace Ryujinx.Graphics.Metal return; } - // Mark dirty - _currentState.Dirty |= DirtyFlags.DepthClamp; + SignalDirty(DirtyFlags.DepthClamp); } - // Inlineable public readonly void UpdateDepthBias(float depthBias, float slopeScale, float clamp) { _currentState.DepthBias = depthBias; @@ -619,11 +616,9 @@ namespace Ryujinx.Graphics.Metal return; } - // Mark dirty - _currentState.Dirty |= DirtyFlags.DepthBias; + SignalDirty(DirtyFlags.DepthBias); } - // Inlineable public void UpdateScissors(ReadOnlySpan> regions) { for (int i = 0; i < regions.Length; i++) @@ -646,11 +641,9 @@ namespace Ryujinx.Graphics.Metal return; } - // Mark dirty - _currentState.Dirty |= DirtyFlags.Scissors; + SignalDirty(DirtyFlags.Scissors); } - // Inlineable public void UpdateViewports(ReadOnlySpan viewports) { static float Clamp(float value) @@ -680,8 +673,7 @@ namespace Ryujinx.Graphics.Metal return; } - // Mark dirty - _currentState.Dirty |= DirtyFlags.Viewports; + SignalDirty(DirtyFlags.Viewports); } public readonly void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) @@ -708,8 +700,7 @@ namespace Ryujinx.Graphics.Metal // Update the buffers on the pipeline UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); - // Mark dirty - _currentState.Dirty |= DirtyFlags.RenderPipeline; + SignalDirty(DirtyFlags.RenderPipeline); } public readonly void UpdateUniformBuffers(ReadOnlySpan buffers) @@ -726,7 +717,7 @@ namespace Ryujinx.Graphics.Metal _currentState.UniformBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); } - _currentState.Dirty |= DirtyFlags.Uniforms; + SignalDirty(DirtyFlags.Uniforms); } public readonly void UpdateStorageBuffers(ReadOnlySpan buffers) @@ -743,7 +734,7 @@ namespace Ryujinx.Graphics.Metal _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); } - _currentState.Dirty |= DirtyFlags.Storages; + SignalDirty(DirtyFlags.Storages); } public readonly void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) @@ -756,10 +747,9 @@ namespace Ryujinx.Graphics.Metal _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer); } - _currentState.Dirty |= DirtyFlags.Storages; + SignalDirty(DirtyFlags.Storages); } - // Inlineable public void UpdateCullMode(bool enable, Face face) { var dirtyScissor = (face == Face.FrontAndBack) != _currentState.CullBoth; @@ -776,15 +766,14 @@ namespace Ryujinx.Graphics.Metal } // Mark dirty - _currentState.Dirty |= DirtyFlags.CullMode; + SignalDirty(DirtyFlags.CullMode); if (dirtyScissor) { - _currentState.Dirty |= DirtyFlags.Scissors; + SignalDirty(DirtyFlags.Scissors); } } - // Inlineable public readonly void UpdateFrontFace(FrontFace frontFace) { _currentState.Winding = frontFace.Convert(); @@ -796,8 +785,7 @@ namespace Ryujinx.Graphics.Metal return; } - // Mark dirty - _currentState.Dirty |= DirtyFlags.FrontFace; + SignalDirty(DirtyFlags.FrontFace); } private readonly void UpdateStencilRefValue(int frontRef, int backRef) @@ -811,8 +799,7 @@ namespace Ryujinx.Graphics.Metal SetStencilRefValue(renderCommandEncoder); } - // Mark dirty - _currentState.Dirty |= DirtyFlags.StencilRef; + SignalDirty(DirtyFlags.StencilRef); } public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) @@ -826,7 +813,7 @@ namespace Ryujinx.Graphics.Metal _currentState.TextureRefs[binding] = default; } - _currentState.Dirty |= DirtyFlags.Textures; + SignalDirty(DirtyFlags.Textures); } public readonly void UpdateImage(ShaderStage stage, ulong binding, TextureBase texture) @@ -840,7 +827,7 @@ namespace Ryujinx.Graphics.Metal _currentState.ImageRefs[binding] = default; } - _currentState.Dirty |= DirtyFlags.Images; + SignalDirty(DirtyFlags.Images); } public void UpdateTextureArray(ShaderStage stage, ulong binding, TextureArray array) @@ -851,19 +838,19 @@ namespace Ryujinx.Graphics.Metal { arrayRef = new EncoderState.ArrayRef(stage, array); - _currentState.Dirty |= DirtyFlags.Textures; + SignalDirty(DirtyFlags.Textures); } } public void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array) { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, setIndex); + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayExtraRefs, setIndex - MetalRenderer.TotalSets); if (arrayRef.Stage != stage || arrayRef.Array != array) { arrayRef = new EncoderState.ArrayRef(stage, array); - _currentState.Dirty |= DirtyFlags.Textures; + SignalDirty(DirtyFlags.Textures); } } @@ -875,19 +862,19 @@ namespace Ryujinx.Graphics.Metal { arrayRef = new EncoderState.ArrayRef(stage, array); - _currentState.Dirty |= DirtyFlags.Images; + SignalDirty(DirtyFlags.Images); } } public void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array) { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex); + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex - MetalRenderer.TotalSets); if (arrayRef.Stage != stage || arrayRef.Array != array) { arrayRef = new EncoderState.ArrayRef(stage, array); - _currentState.Dirty |= DirtyFlags.Images; + SignalDirty(DirtyFlags.Images); } } @@ -1054,7 +1041,7 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex); } - private readonly void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, int setIndex) + private readonly void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, uint setIndex) { var bindingSegments = program.BindingSegments[setIndex]; @@ -1089,7 +1076,7 @@ namespace Ryujinx.Graphics.Metal switch (setIndex) { - case MetalRenderer.UniformSetIndex: + case Constants.ConstantBuffersSetIndex: for (int i = 0; i < count; i++) { int index = binding + i; @@ -1139,7 +1126,7 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); } break; - case MetalRenderer.StorageSetIndex: + case Constants.StorageBuffersSetIndex: for (int i = 0; i < count; i++) { int index = binding + i; @@ -1170,7 +1157,7 @@ namespace Ryujinx.Graphics.Metal MTLRenderStages renderStages = 0; - if (segment.Stages.HasFlag(ResourceStages.Vertex)) + if ((segment.Stages & ResourceStages.Vertex) != 0) { vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; vertResourceIdIndex++; @@ -1178,7 +1165,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageVertex; } - if (segment.Stages.HasFlag(ResourceStages.Fragment)) + if ((segment.Stages & ResourceStages.Fragment) != 0) { fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; fragResourceIdIndex++; @@ -1189,7 +1176,7 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); } break; - case MetalRenderer.TextureSetIndex: + case Constants.TexturesSetIndex: if (!segment.IsArray) { for (int i = 0; i < count; i++) @@ -1247,10 +1234,106 @@ namespace Ryujinx.Graphics.Metal } else { - // TODO: Texture arrays + var textureArray = _currentState.TextureArrayRefs[binding].Array; + + if (segment.Type != ResourceType.BufferTexture) + { + var textures = textureArray.GetTextureRefs(); + var samplers = new Sampler[textures.Length]; + + for (int i = 0; i < textures.Length; i++) + { + TextureRef texture = textures[i]; + + if (texture.Storage == null) + { + continue; + } + + var mtlTexture = texture.Storage.GetHandle(); + samplers[i] = texture.Sampler; + + MTLRenderStages renderStages = 0; + + if ((segment.Stages & ResourceStages.Vertex) != 0) + { + vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if ((segment.Stages & ResourceStages.Fragment) != 0) + { + fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), + MTLResourceUsage.Read, renderStages); + } + + foreach (var sampler in samplers) + { + if (sampler == null) + { + continue; + } + + if ((segment.Stages & ResourceStages.Vertex) != 0) + { + vertResourceIds[vertResourceIdIndex] = sampler.GetSampler().GpuResourceID._impl; + vertResourceIdIndex++; + } + + if ((segment.Stages & ResourceStages.Fragment) != 0) + { + fragResourceIds[fragResourceIdIndex] = sampler.GetSampler().GpuResourceID._impl; + fragResourceIdIndex++; + } + } + } + else + { + var bufferTextures = textureArray.GetBufferTextureRefs(); + + foreach (TextureBuffer bufferTexture in bufferTextures) + { + if (bufferTexture == null) + { + continue; + } + + bufferTexture.RebuildStorage(false); + + var mtlTexture = bufferTexture.GetHandle(); + + MTLRenderStages renderStages = 0; + + if ((segment.Stages & ResourceStages.Vertex) != 0) + { + vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if ((segment.Stages & ResourceStages.Fragment) != 0) + { + fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages); + } + } } break; - case MetalRenderer.ImageSetIndex: + case Constants.ImagesSetIndex: if (!segment.IsArray) { for (int i = 0; i < count; i++) @@ -1306,7 +1389,7 @@ namespace Ryujinx.Graphics.Metal } } - private readonly void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, int setIndex) + private readonly void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, uint setIndex) { var bindingSegments = program.BindingSegments[setIndex]; @@ -1332,7 +1415,7 @@ namespace Ryujinx.Graphics.Metal switch (setIndex) { - case MetalRenderer.UniformSetIndex: + case Constants.ConstantBuffersSetIndex: for (int i = 0; i < count; i++) { int index = binding + i; @@ -1369,7 +1452,7 @@ namespace Ryujinx.Graphics.Metal } } break; - case MetalRenderer.StorageSetIndex: + case Constants.StorageBuffersSetIndex: for (int i = 0; i < count; i++) { int index = binding + i; @@ -1406,7 +1489,7 @@ namespace Ryujinx.Graphics.Metal } } break; - case MetalRenderer.TextureSetIndex: + case Constants.TexturesSetIndex: if (!segment.IsArray) { for (int i = 0; i < count; i++) @@ -1429,7 +1512,7 @@ namespace Ryujinx.Graphics.Metal var mtlTexture = storage.GetHandle(); - if (segment.Stages.HasFlag(ResourceStages.Compute)) + if ((segment.Stages & ResourceStages.Compute) != 0) { computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; @@ -1445,10 +1528,70 @@ namespace Ryujinx.Graphics.Metal } else { - // TODO: Texture arrays + var textureArray = _currentState.TextureArrayRefs[binding].Array; + + if (segment.Type != ResourceType.BufferTexture) + { + var textures = textureArray.GetTextureRefs(); + var samplers = new Sampler[textures.Length]; + + for (int i = 0; i < textures.Length; i++) + { + TextureRef texture = textures[i]; + + if (texture.Storage == null) + { + continue; + } + + var mtlTexture = texture.Storage.GetHandle(); + + if ((segment.Stages & ResourceStages.Compute) != 0) + { + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), + MTLResourceUsage.Read); + resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resourceIdIndex++; + + samplers[i] = texture.Sampler; + } + } + + foreach (var sampler in samplers) + { + if (sampler != null) + { + resourceIds[resourceIdIndex] = sampler.GetSampler().GpuResourceID._impl; + resourceIdIndex++; + } + } + } + else + { + var bufferTextures = textureArray.GetBufferTextureRefs(); + + foreach (TextureBuffer bufferTexture in bufferTextures) + { + if (bufferTexture == null) + { + continue; + } + + bufferTexture.RebuildStorage(false); + + var mtlTexture = bufferTexture.GetHandle(); + + if ((segment.Stages & ResourceStages.Compute) != 0) + { + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); + resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resourceIdIndex++; + } + } + } } break; - case MetalRenderer.ImageSetIndex: + case Constants.ImagesSetIndex: if (!segment.IsArray) { if (segment.Type != ResourceType.BufferTexture) @@ -1468,7 +1611,7 @@ namespace Ryujinx.Graphics.Metal var mtlTexture = storage.GetHandle(); - if (segment.Stages.HasFlag(ResourceStages.Compute)) + if ((segment.Stages & ResourceStages.Compute) != 0) { computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; @@ -1489,14 +1632,14 @@ namespace Ryujinx.Graphics.Metal } } - private static uint SetIndexToBindingIndex(int setIndex) + private static uint SetIndexToBindingIndex(uint setIndex) { return setIndex switch { - MetalRenderer.UniformSetIndex => Constants.ConstantBuffersIndex, - MetalRenderer.StorageSetIndex => Constants.StorageBuffersIndex, - MetalRenderer.TextureSetIndex => Constants.TexturesIndex, - MetalRenderer.ImageSetIndex => Constants.ImagesIndex, + Constants.ConstantBuffersSetIndex => Constants.ConstantBuffersIndex, + Constants.StorageBuffersSetIndex => Constants.StorageBuffersIndex, + Constants.TexturesSetIndex => Constants.TexturesIndex, + Constants.ImagesSetIndex => Constants.ImagesIndex, }; } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index ed9a7f656..7e20ec221 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -123,7 +123,16 @@ namespace Ryujinx.Graphics.Metal private static string ReadMsl(string fileName) { - return EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName)); + var msl = EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName)); + +#pragma warning disable IDE0055 // Disable formatting + msl = msl.Replace("CONSTANT_BUFFERS_INDEX", $"{Constants.ConstantBuffersIndex}") + .Replace("STORAGE_BUFFERS_INDEX", $"{Constants.StorageBuffersIndex}") + .Replace("TEXTURES_INDEX", $"{Constants.TexturesIndex}") + .Replace("IMAGES_INDEX", $"{Constants.ImagesIndex}"); +#pragma warning restore IDE0055 + + return msl; } public unsafe void BlitColor( diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index cd2a83bff..35e721e94 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -13,11 +13,6 @@ namespace Ryujinx.Graphics.Metal { public const int TotalSets = 4; - public const int UniformSetIndex = 0; - public const int StorageSetIndex = 1; - public const int TextureSetIndex = 2; - public const int ImageSetIndex = 3; - private readonly MTLDevice _device; private readonly MTLCommandQueue _queue; private readonly Func _getMetalLayer; @@ -181,8 +176,7 @@ namespace Ryujinx.Graphics.Metal supportsCubemapView: true, supportsNonConstantTextureOffset: false, supportsQuads: false, - // TODO: Metal Bindless Support - supportsSeparateSampler: false, + supportsSeparateSampler: true, supportsShaderBallot: false, supportsShaderBarrierDivergence: false, supportsShaderFloat64: false, @@ -194,12 +188,12 @@ namespace Ryujinx.Graphics.Metal supportsViewportSwizzle: false, supportsIndirectParameters: true, supportsDepthClipControl: false, - uniformBufferSetIndex: UniformSetIndex, - storageBufferSetIndex: StorageSetIndex, - textureSetIndex: TextureSetIndex, - imageSetIndex: ImageSetIndex, - extraSetBaseIndex: 0, - maximumExtraSets: 0, + uniformBufferSetIndex: (int)Constants.ConstantBuffersSetIndex, + storageBufferSetIndex: (int)Constants.StorageBuffersSetIndex, + textureSetIndex: (int)Constants.TexturesSetIndex, + imageSetIndex: (int)Constants.ImagesSetIndex, + extraSetBaseIndex: TotalSets, + maximumExtraSets: (int)Constants.MaximumExtraSets, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage, diff --git a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs index e969ce82b..36ae9bac6 100644 --- a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs +++ b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs @@ -27,12 +27,12 @@ namespace Ryujinx.Graphics.Metal public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false) { - int setIndex = type switch + uint setIndex = type switch { - ResourceType.UniformBuffer => MetalRenderer.UniformSetIndex, - ResourceType.StorageBuffer => MetalRenderer.StorageSetIndex, - ResourceType.TextureAndSampler or ResourceType.BufferTexture => MetalRenderer.TextureSetIndex, - ResourceType.Image or ResourceType.BufferImage => MetalRenderer.ImageSetIndex, + ResourceType.UniformBuffer => Constants.ConstantBuffersSetIndex, + ResourceType.StorageBuffer => Constants.StorageBuffersSetIndex, + ResourceType.TextureAndSampler or ResourceType.BufferTexture => Constants.TexturesSetIndex, + ResourceType.Image or ResourceType.BufferImage => Constants.ImagesSetIndex, _ => throw new ArgumentException($"Invalid resource type \"{type}\"."), }; diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index a5e4e8170..7caf0c846 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -22,7 +22,7 @@ struct Textures }; vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], - constant ConstantBuffers &constant_buffers [[buffer(20)]]) { + constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { CopyVertexOut out; int low = vid & 1; @@ -38,6 +38,6 @@ vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], } fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(22)]]) { + constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { return textures.texture.sample(textures.sampler, in.uv); } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal index 09c5d76ca..86ee306d3 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal @@ -13,7 +13,7 @@ struct Textures }; fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(22)]], + constant Textures &textures [[buffer(TEXTURES_INDEX)]], uint sample_id [[sample_id]]) { uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); uint2 tex_coord = uint2(in.uv * float2(tex_size)); diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal index 492a27d21..4424ac531 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal @@ -23,8 +23,8 @@ struct StorageBuffers { device OutData* out_data; }; -kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(20)]], - device StorageBuffers &storage_buffers [[buffer(21)]], +kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]], + device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], uint3 thread_position_in_grid [[thread_position_in_grid]], uint3 threads_per_threadgroup [[threads_per_threadgroup]], uint3 threadgroups_per_grid [[threads_per_grid]]) diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal index d3ef9603f..306fad87a 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal @@ -33,6 +33,6 @@ struct FragmentOut { }; fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant ConstantBuffers &constant_buffers [[buffer(20)]]) { + constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { return {constant_buffers.clear_color->data}; } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal index c6b547be8..8b8467c2f 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal @@ -18,7 +18,7 @@ struct FragmentOut { }; fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(22)]]) { + constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { FragmentOut out; out.depth = textures.texture.sample(textures.sampler, in.uv).r; diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal index 9fb5e6e50..10791f636 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal @@ -17,7 +17,7 @@ struct FragmentOut { }; fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(22)]], + constant Textures &textures [[buffer(TEXTURES_INDEX)]], uint sample_id [[sample_id]]) { FragmentOut out; diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal index 4ee4f4a51..7e50f2ce7 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal @@ -33,7 +33,7 @@ vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { } fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant ConstantBuffers &constant_buffers [[buffer(20)]]) { + constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { FragmentOut out; out.depth = constant_buffers.clear_depth->data; diff --git a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal index da7c6e90a..0b25f322d 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal @@ -18,7 +18,7 @@ struct FragmentOut { }; fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(22)]]) { + constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { FragmentOut out; out.stencil = textures.texture.sample(textures.sampler, in.uv).r; diff --git a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal index 3948eacc7..e7f2d20b7 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal @@ -17,7 +17,7 @@ struct FragmentOut { }; fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(22)]], + constant Textures &textures [[buffer(TEXTURES_INDEX)]], uint sample_id [[sample_id]]) { FragmentOut out; diff --git a/src/Ryujinx.Graphics.Metal/TextureArray.cs b/src/Ryujinx.Graphics.Metal/TextureArray.cs index 762e6a5fd..cfca843f7 100644 --- a/src/Ryujinx.Graphics.Metal/TextureArray.cs +++ b/src/Ryujinx.Graphics.Metal/TextureArray.cs @@ -73,6 +73,16 @@ namespace Ryujinx.Graphics.Metal SetDirty(); } + public TextureRef[] GetTextureRefs() + { + return _textureRefs; + } + + public TextureBuffer[] GetBufferTextureRefs() + { + return _bufferTextureRefs; + } + private void SetDirty() { _pipeline.DirtyTextures(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index e31e397c1..e05c30282 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl * */ - public static void Declare(CodeGenContext context, StructuredProgramInfo info) + public static int[] Declare(CodeGenContext context, StructuredProgramInfo info) { // TODO: Re-enable this warning context.AppendLine("#pragma clang diagnostic ignored \"-Wunused-variable\""); @@ -75,10 +75,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(); DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); context.AppendLine(); - DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values, true, fsi); - DeclareBufferStructures(context, context.Properties.StorageBuffers.Values, false, fsi); - DeclareTextures(context, context.Properties.Textures.Values); - DeclareImages(context, context.Properties.Images.Values, fsi); + DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values.OrderBy(x => x.Binding).ToArray(), true, fsi); + DeclareBufferStructures(context, context.Properties.StorageBuffers.Values.OrderBy(x => x.Binding).ToArray(), false, fsi); + + // We need to declare each set as a new struct + var textureDefinitions = context.Properties.Textures.Values + .GroupBy(x => x.Set) + .ToDictionary(x => x.Key, x => x.OrderBy(y => y.Binding).ToArray()); + + var imageDefinitions = context.Properties.Images.Values + .GroupBy(x => x.Set) + .ToDictionary(x => x.Key, x => x.OrderBy(y => y.Binding).ToArray()); + + var textureSets = textureDefinitions.Keys.ToArray(); + var imageSets = imageDefinitions.Keys.ToArray(); + + var sets = textureSets.Union(imageSets).ToArray(); + + foreach (var set in textureDefinitions) + { + DeclareTextures(context, set.Value, set.Key); + } + + foreach (var set in imageDefinitions) + { + DeclareImages(context, set.Value, set.Key, fsi); + } if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) { @@ -99,6 +121,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal"); } + + return sets; } static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) @@ -186,22 +210,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } - private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers, bool constant, bool fsi) + private static void DeclareBufferStructures(CodeGenContext context, BufferDefinition[] buffers, bool constant, bool fsi) { var name = constant ? "ConstantBuffers" : "StorageBuffers"; var addressSpace = constant ? "constant" : "device"; - List argBufferPointers = []; + string[] bufferDec = new string[buffers.Length]; - // TODO: Avoid Linq if we can - var sortedBuffers = buffers.OrderBy(x => x.Binding).ToArray(); - - foreach (BufferDefinition buffer in sortedBuffers) + for (int i = 0; i < buffers.Length; i++) { + BufferDefinition buffer = buffers[i]; + var needsPadding = buffer.Layout == BufferLayout.Std140; string fsiSuffix = constant && fsi ? " [[raster_order_group(0)]]" : ""; - argBufferPointers.Add($"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};"); + bufferDec[i] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};"; context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); context.EnterScope(); @@ -209,7 +232,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl foreach (StructureField field in buffer.Type.Fields) { var type = field.Type; - type |= (needsPadding && (field.Type & AggregateType.Array) != 0) ? AggregateType.Vector4 : AggregateType.Invalid; + type |= (needsPadding && (field.Type & AggregateType.Array) != 0) + ? AggregateType.Vector4 + : AggregateType.Invalid; type &= ~AggregateType.Array; @@ -239,66 +264,85 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine($"struct {name}"); context.EnterScope(); - foreach (var pointer in argBufferPointers) + foreach (var declaration in bufferDec) { - context.AppendLine(pointer); + context.AppendLine(declaration); } context.LeaveScope(";"); context.AppendLine(); } - private static void DeclareTextures(CodeGenContext context, IEnumerable textures) + private static void DeclareTextures(CodeGenContext context, TextureDefinition[] textures, int set) { - context.AppendLine("struct Textures"); + var setName = GetNameForSet(set); + context.AppendLine($"struct {setName}"); context.EnterScope(); - List argBufferPointers = []; + List textureDec = []; - // TODO: Avoid Linq if we can - var sortedTextures = textures.OrderBy(x => x.Binding).ToArray(); - - foreach (TextureDefinition texture in sortedTextures) + foreach (TextureDefinition texture in textures) { - var textureTypeName = texture.Type.ToMslTextureType(); - argBufferPointers.Add($"{textureTypeName} tex_{texture.Name};"); + if (texture.Type != SamplerType.None) + { + var textureTypeName = texture.Type.ToMslTextureType(); + + if (texture.ArrayLength > 1) + { + textureTypeName = $"array<{textureTypeName}, {texture.ArrayLength}>"; + } + + textureDec.Add($"{textureTypeName} tex_{texture.Name};"); + } if (!texture.Separate && texture.Type != SamplerType.TextureBuffer) { - argBufferPointers.Add($"sampler samp_{texture.Name};"); + var samplerType = "sampler"; + + if (texture.ArrayLength > 1) + { + samplerType = $"array<{samplerType}, {texture.ArrayLength}>"; + } + + textureDec.Add($"{samplerType} samp_{texture.Name};"); } } - foreach (var pointer in argBufferPointers) + foreach (var declaration in textureDec) { - context.AppendLine(pointer); + context.AppendLine(declaration); } context.LeaveScope(";"); context.AppendLine(); } - private static void DeclareImages(CodeGenContext context, IEnumerable images, bool fsi) + private static void DeclareImages(CodeGenContext context, TextureDefinition[] images, int set, bool fsi) { - context.AppendLine("struct Images"); + var setName = GetNameForSet(set); + context.AppendLine($"struct {setName}"); context.EnterScope(); - List argBufferPointers = []; + string[] imageDec = new string[images.Length]; - // TODO: Avoid Linq if we can - var sortedImages = images.OrderBy(x => x.Binding).ToArray(); - - foreach (TextureDefinition image in sortedImages) + for (int i = 0; i < images.Length; i++) { + TextureDefinition image = images[i]; + var imageTypeName = image.Type.ToMslTextureType(true); + if (image.ArrayLength > 1) + { + imageTypeName = $"array<{imageTypeName}, {image.ArrayLength}>"; + } + string fsiSuffix = fsi ? " [[raster_order_group(0)]]" : ""; - argBufferPointers.Add($"{imageTypeName} {image.Name}{fsiSuffix};"); + imageDec[i] = $"{imageTypeName} {image.Name}{fsiSuffix};"; } - foreach (var pointer in argBufferPointers) + foreach (var declaration in imageDec) { - context.AppendLine(pointer); + context.AppendLine(declaration); } context.LeaveScope(";"); @@ -483,5 +527,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(code); context.AppendLine(); } + + public static string GetNameForSet(int set, bool forVar = false) + { + return (uint)set switch + { + Defaults.TexturesSetIndex => forVar ? "textures" : "Textures", + Defaults.ImagesSetIndex => forVar ? "images" : "Images", + _ => $"{(forVar ? "set" : "Set")}{set}" + }; + } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs index a78de36ce..511a2f606 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs @@ -14,14 +14,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string UndefinedName = "0"; - public const int MaxUniformBuffersPerStage = 18; - public const int MaxStorageBuffersPerStage = 16; - public const int MaxTexturesPerStage = 64; + public const int MaxVertexBuffers = 16; - public const uint ConstantBuffersIndex = 20; - public const uint StorageBuffersIndex = 21; - public const uint TexturesIndex = 22; - public const uint ImagesIndex = 23; + public const uint ZeroBufferIndex = MaxVertexBuffers; + public const uint BaseSetIndex = MaxVertexBuffers + 1; + + public const uint ConstantBuffersIndex = BaseSetIndex; + public const uint StorageBuffersIndex = BaseSetIndex + 1; + public const uint TexturesIndex = BaseSetIndex + 2; + public const uint ImagesIndex = BaseSetIndex + 3; + + public const uint ConstantBuffersSetIndex = 0; + public const uint StorageBuffersSetIndex = 1; + public const uint TexturesSetIndex = 2; + public const uint ImagesSetIndex = 3; public const int TotalClipDistances = 8; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 198d0cf8d..f6fa7aa73 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -494,13 +494,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { TextureDefinition textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()]; string name = textureDefinition.Name; + string setName = Declarations.GetNameForSet(textureDefinition.Set, true); if (textureDefinition.ArrayLength != 1) { name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; } - return $"textures.tex_{name}"; + return $"{setName}.tex_{name}"; } private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) @@ -510,26 +511,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions TextureDefinition samplerDefinition = context.Properties.Textures[index]; string name = samplerDefinition.Name; + string setName = Declarations.GetNameForSet(samplerDefinition.Set, true); if (samplerDefinition.ArrayLength != 1) { name = $"{name}[{GetSourceExpr(context, texOp.GetSource(sourceIndex), AggregateType.S32)}]"; } - return $"textures.samp_{name}"; + return $"{setName}.samp_{name}"; } private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) { - TextureDefinition definition = context.Properties.Images[texOp.GetTextureSetAndBinding()]; - string name = definition.Name; + TextureDefinition imageDefinition = context.Properties.Images[texOp.GetTextureSetAndBinding()]; + string name = imageDefinition.Name; + string setName = Declarations.GetNameForSet(imageDefinition.Set, true); - if (definition.ArrayLength != 1) + if (imageDefinition.ArrayLength != 1) { name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; } - return $"images.{name}"; + return $"{setName}.{name}"; } private static string GetMaskMultiDest(int mask) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index 28a69c508..7de6ee5dd 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -20,28 +20,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl CodeGenContext context = new(info, parameters); - Declarations.Declare(context, info); + var sets = Declarations.Declare(context, info); if (info.Functions.Count != 0) { for (int i = 1; i < info.Functions.Count; i++) { - PrintFunction(context, info.Functions[i], parameters.Definitions.Stage); + PrintFunction(context, info.Functions[i], parameters.Definitions.Stage, sets); context.AppendLine(); } } - PrintFunction(context, info.Functions[0], parameters.Definitions.Stage, true); + PrintFunction(context, info.Functions[0], parameters.Definitions.Stage, sets, true); return context.GetCode(); } - private static void PrintFunction(CodeGenContext context, StructuredFunction function, ShaderStage stage, bool isMainFunc = false) + private static void PrintFunction(CodeGenContext context, StructuredFunction function, ShaderStage stage, int[] sets, bool isMainFunc = false) { context.CurrentFunction = function; - context.AppendLine(GetFunctionSignature(context, function, stage, isMainFunc)); + context.AppendLine(GetFunctionSignature(context, function, stage, sets, isMainFunc)); context.EnterScope(); Declarations.DeclareLocals(context, function, stage, isMainFunc); @@ -61,6 +61,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl CodeGenContext context, StructuredFunction function, ShaderStage stage, + int[] sets, bool isMainFunc = false) { int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); @@ -166,8 +167,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl args = args.Append($"constant ConstantBuffers &constant_buffers [[buffer({Defaults.ConstantBuffersIndex})]]").ToArray(); args = args.Append($"device StorageBuffers &storage_buffers [[buffer({Defaults.StorageBuffersIndex})]]").ToArray(); - args = args.Append($"constant Textures &textures [[buffer({Defaults.TexturesIndex})]]").ToArray(); - args = args.Append($"constant Images &images [[buffer({Defaults.ImagesIndex})]]").ToArray(); + + foreach (var set in sets) + { + var bindingIndex = set + Defaults.BaseSetIndex; + args = args.Append($"constant {Declarations.GetNameForSet(set)} &{Declarations.GetNameForSet(set, true)} [[buffer({bindingIndex})]]").ToArray(); + } } var funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}("; diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index 44ff13294..49c5222e4 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -192,7 +192,7 @@ namespace Ryujinx.Graphics.Shader typeName += "_array"; } - return $"{typeName} "; + return $"{typeName}"; } } } -- 2.47.1 From 68146fa285d087dcdc862d41f327d98f4a7abe92 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 00:37:37 +0100 Subject: [PATCH 321/368] Helper Shader fixes for non float formats --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 155 +++++++++++++++--- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- src/Ryujinx.Graphics.Metal/Shaders/Blit.metal | 4 +- .../Shaders/BlitMs.metal | 32 +++- .../Shaders/ColorClear.metal | 4 +- 5 files changed, 166 insertions(+), 31 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 7e20ec221..87d720c5e 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -21,9 +21,15 @@ namespace Ryujinx.Graphics.Metal private readonly ISampler _samplerLinear; private readonly ISampler _samplerNearest; - private readonly IProgram _programColorBlit; - private readonly IProgram _programColorBlitMs; - private readonly List _programsColorClear = new(); + private readonly IProgram _programColorBlitF; + private readonly IProgram _programColorBlitI; + private readonly IProgram _programColorBlitU; + private readonly IProgram _programColorBlitMsF; + private readonly IProgram _programColorBlitMsI; + private readonly IProgram _programColorBlitMsU; + private readonly List _programsColorClearF = new(); + private readonly List _programsColorClearI = new(); + private readonly List _programsColorClearU = new(); private readonly IProgram _programDepthStencilClear; private readonly IProgram _programStrideChange; private readonly IProgram _programDepthBlit; @@ -47,27 +53,80 @@ namespace Ryujinx.Graphics.Metal .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build(); var blitSource = ReadMsl("Blit.metal"); - _programColorBlit = new Program( + + var blitSourceF = blitSource.Replace("FORMAT", "float", StringComparison.Ordinal); + _programColorBlitF = new Program( [ - new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + new ShaderSource(blitSourceF, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); + + var blitSourceI = blitSource.Replace("FORMAT", "int"); + _programColorBlitI = new Program( + [ + new ShaderSource(blitSourceI, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSourceI, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); + + var blitSourceU = blitSource.Replace("FORMAT", "uint"); + _programColorBlitU = new Program( + [ + new ShaderSource(blitSourceU, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSourceU, ShaderStage.Vertex, TargetLanguage.Msl) ], blitResourceLayout, device); var blitMsSource = ReadMsl("BlitMs.metal"); - _programColorBlitMs = new Program( + + var blitMsSourceF = blitMsSource.Replace("FORMAT", "float"); + _programColorBlitMsF = new Program( [ - new ShaderSource(blitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + new ShaderSource(blitMsSourceF, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitMsSourceF, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); + + var blitMsSourceI = blitMsSource.Replace("FORMAT", "int"); + _programColorBlitMsI = new Program( + [ + new ShaderSource(blitMsSourceI, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitMsSourceI, ShaderStage.Vertex, TargetLanguage.Msl) + ], blitResourceLayout, device); + + var blitMsSourceU = blitMsSource.Replace("FORMAT", "uint"); + _programColorBlitMsU = new Program( + [ + new ShaderSource(blitMsSourceU, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitMsSourceU, ShaderStage.Vertex, TargetLanguage.Msl) ], blitResourceLayout, device); var colorClearResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build(); var colorClearSource = ReadMsl("ColorClear.metal"); + for (int i = 0; i < Constants.MaxColorAttachments; i++) { - var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()); - _programsColorClear.Add(new Program( + var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "float"); + _programsColorClearF.Add(new Program( + [ + new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], colorClearResourceLayout, device)); + } + + for (int i = 0; i < Constants.MaxColorAttachments; i++) + { + var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "int"); + _programsColorClearI.Add(new Program( + [ + new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], colorClearResourceLayout, device)); + } + + for (int i = 0; i < Constants.MaxColorAttachments; i++) + { + var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "uint"); + _programsColorClearU.Add(new Program( [ new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) @@ -96,28 +155,28 @@ namespace Ryujinx.Graphics.Metal _programDepthBlit = new Program( [ new ShaderSource(depthBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) ], blitResourceLayout, device); var depthBlitMsSource = ReadMsl("DepthBlitMs.metal"); _programDepthBlitMs = new Program( [ new ShaderSource(depthBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) ], blitResourceLayout, device); var stencilBlitSource = ReadMsl("StencilBlit.metal"); _programStencilBlit = new Program( [ new ShaderSource(stencilBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) ], blitResourceLayout, device); var stencilBlitMsSource = ReadMsl("StencilBlitMs.metal"); _programStencilBlitMs = new Program( [ new ShaderSource(stencilBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) ], blitResourceLayout, device); } @@ -201,11 +260,33 @@ namespace Ryujinx.Graphics.Metal } else if (src.Info.Target.IsMultisample()) { - _pipeline.SetProgram(_programColorBlitMs); + if (dst.Info.Format.IsSint()) + { + _pipeline.SetProgram(_programColorBlitMsI); + } + else if (dst.Info.Format.IsUint()) + { + _pipeline.SetProgram(_programColorBlitMsU); + } + else + { + _pipeline.SetProgram(_programColorBlitMsF); + } } else { - _pipeline.SetProgram(_programColorBlit); + if (dst.Info.Format.IsSint()) + { + _pipeline.SetProgram(_programColorBlitI); + } + else if (dst.Info.Format.IsUint()) + { + _pipeline.SetProgram(_programColorBlitU); + } + else + { + _pipeline.SetProgram(_programColorBlitF); + } } int dstWidth = dst.Width; @@ -438,7 +519,7 @@ namespace Ryujinx.Graphics.Metal 0f, 1f); - _pipeline.SetProgram(_programColorBlit); + _pipeline.SetProgram(_programColorBlitF); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); @@ -502,7 +583,8 @@ namespace Ryujinx.Graphics.Metal ReadOnlySpan clearColor, uint componentMask, int dstWidth, - int dstHeight) + int dstHeight, + Format format) { // Keep original scissor DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors); @@ -536,7 +618,19 @@ namespace Ryujinx.Graphics.Metal Span componentMasks = stackalloc uint[index + 1]; componentMasks[index] = componentMask; - _pipeline.SetProgram(_programsColorClear[index]); + if (format.IsSint()) + { + _pipeline.SetProgram(_programsColorClearI[index]); + } + else if (format.IsUint()) + { + _pipeline.SetProgram(_programsColorClearU[index]); + } + else + { + _pipeline.SetProgram(_programsColorClearF[index]); + } + _pipeline.SetBlendState(index, new BlendDescriptor()); _pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); @@ -630,11 +724,28 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { - _programColorBlit.Dispose(); - foreach (var programColorClear in _programsColorClear) + _programColorBlitF.Dispose(); + _programColorBlitI.Dispose(); + _programColorBlitU.Dispose(); + _programColorBlitMsF.Dispose(); + _programColorBlitMsI.Dispose(); + _programColorBlitMsU.Dispose(); + + foreach (var programColorClear in _programsColorClearF) { programColorClear.Dispose(); } + + foreach (var programColorClear in _programsColorClearU) + { + programColorClear.Dispose(); + } + + foreach (var programColorClear in _programsColorClearI) + { + programColorClear.Dispose(); + } + _programDepthStencilClear.Dispose(); _pipeline.Dispose(); _samplerLinear.Dispose(); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 39f85d205..15b87fb46 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -269,7 +269,7 @@ namespace Ryujinx.Graphics.Metal return; } - _renderer.HelperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height); + _renderer.HelperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height, dst.Info.Format); } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index 7caf0c846..887878499 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -17,7 +17,7 @@ struct ConstantBuffers { struct Textures { - texture2d texture; + texture2d texture; sampler sampler; }; @@ -37,7 +37,7 @@ vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], return out; } -fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], +fragment FORMAT4 fragmentMain(CopyVertexOut in [[stage_in]], constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { return textures.texture.sample(textures.sampler, in.uv); } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal index 86ee306d3..1077b6cea 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal @@ -7,12 +7,36 @@ struct CopyVertexOut { float2 uv; }; -struct Textures -{ - texture2d_ms texture; +struct TexCoords { + float data[4]; }; -fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], +struct ConstantBuffers { + constant TexCoords* tex_coord; +}; + +struct Textures +{ + texture2d_ms texture; +}; + +vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], + constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { + CopyVertexOut out; + + int low = vid & 1; + int high = vid >> 1; + out.uv.x = constant_buffers.tex_coord->data[low]; + out.uv.y = constant_buffers.tex_coord->data[2 + high]; + out.position.x = (float(low) - 0.5f) * 2.0f; + out.position.y = (float(high) - 0.5f) * 2.0f; + out.position.z = 0.0f; + out.position.w = 1.0f; + + return out; +} + +fragment FORMAT4 fragmentMain(CopyVertexOut in [[stage_in]], constant Textures &textures [[buffer(TEXTURES_INDEX)]], uint sample_id [[sample_id]]) { uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal index 306fad87a..46a57e035 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal @@ -7,7 +7,7 @@ struct VertexOut { }; struct ClearColor { - float4 data; + FORMAT4 data; }; struct ConstantBuffers { @@ -29,7 +29,7 @@ vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { } struct FragmentOut { - float4 color [[color(COLOR_ATTACHMENT_INDEX)]]; + FORMAT4 color [[color(COLOR_ATTACHMENT_INDEX)]]; }; fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], -- 2.47.1 From 341e4e5fb11cdd8edcedf91f5d3bbced208a105a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 11:52:14 +0100 Subject: [PATCH 322/368] Image Constant Fixes Allows Mario Party Superstars to boot --- src/Ryujinx.Graphics.Metal/Constants.cs | 3 +++ src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 ++-- .../EncoderStateManager.cs | 14 ++++++------- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- src/Ryujinx.Graphics.Metal/Pipeline.cs | 20 ++++++------------- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 133925e2d..43baf722a 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -7,9 +7,12 @@ namespace Ryujinx.Graphics.Metal public const int MaxUniformBuffersPerStage = 18; public const int MaxStorageBuffersPerStage = 16; public const int MaxTexturesPerStage = 64; + public const int MaxImagesPerStage = 16; + public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages; public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; + public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages; public const int MaxColorAttachments = 8; public const int MaxViewports = 16; // TODO: Check this value diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 8d408d847..a99a6f35a 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -106,8 +106,8 @@ namespace Ryujinx.Graphics.Metal public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; - public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings]; - public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxTextureBindings]; + public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings * 2]; + public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxImageBindings * 2]; public ArrayRef[] TextureArrayRefs = []; public ArrayRef[] ImageArrayRefs = []; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 41a4140fd..0d3df86c7 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -802,7 +802,7 @@ namespace Ryujinx.Graphics.Metal SignalDirty(DirtyFlags.StencilRef); } - public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) + public readonly void UpdateTextureAndSampler(ShaderStage stage, int binding, TextureBase texture, Sampler sampler) { if (texture != null) { @@ -816,9 +816,9 @@ namespace Ryujinx.Graphics.Metal SignalDirty(DirtyFlags.Textures); } - public readonly void UpdateImage(ShaderStage stage, ulong binding, TextureBase texture) + public readonly void UpdateImage(ShaderStage stage, int binding, TextureBase image) { - if (texture is Texture view) + if (image is Texture view) { _currentState.ImageRefs[binding] = new(stage, view); } @@ -830,9 +830,9 @@ namespace Ryujinx.Graphics.Metal SignalDirty(DirtyFlags.Images); } - public void UpdateTextureArray(ShaderStage stage, ulong binding, TextureArray array) + public void UpdateTextureArray(ShaderStage stage, int binding, TextureArray array) { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, (int)binding, ArrayGrowthSize); + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, binding, ArrayGrowthSize); if (arrayRef.Stage != stage || arrayRef.Array != array) { @@ -854,9 +854,9 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdateImageArray(ShaderStage stage, ulong binding, ImageArray array) + public void UpdateImageArray(ShaderStage stage, int binding, ImageArray array) { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayRefs, (int)binding, ArrayGrowthSize); + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayRefs, binding, ArrayGrowthSize); if (arrayRef.Stage != stage || arrayRef.Array != array) { diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 35e721e94..6bdc043bf 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -197,7 +197,7 @@ namespace Ryujinx.Graphics.Metal maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage, - maximumImagesPerStage: Constants.MaxTextureBindings, + maximumImagesPerStage: Constants.MaxImagesPerStage, maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength, maximumSupportedAnisotropy: 0, shaderSubgroupSize: 256, diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 15b87fb46..a4654d36a 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -540,13 +540,11 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager.UpdateIndexBuffer(buffer, type); } - public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) + public void SetImage(ShaderStage stage, int binding, ITexture image, Format imageFormat) { - if (texture is TextureBase tex) + if (image is TextureBase img) { - var index = (ulong)binding; - - _encoderStateManager.UpdateImage(stage, index, tex); + _encoderStateManager.UpdateImage(stage, binding, img); } } @@ -554,9 +552,7 @@ namespace Ryujinx.Graphics.Metal { if (array is ImageArray imageArray) { - var index = (ulong)binding; - - _encoderStateManager.UpdateImageArray(stage, index, imageArray); + _encoderStateManager.UpdateImageArray(stage, binding, imageArray); } } @@ -665,9 +661,7 @@ namespace Ryujinx.Graphics.Metal { if (sampler == null || sampler is Sampler) { - var index = (ulong)binding; - - _encoderStateManager.UpdateTextureAndSampler(stage, index, tex, (Sampler)sampler); + _encoderStateManager.UpdateTextureAndSampler(stage, binding, tex, (Sampler)sampler); } } } @@ -676,9 +670,7 @@ namespace Ryujinx.Graphics.Metal { if (array is TextureArray textureArray) { - var index = (ulong)binding; - - _encoderStateManager.UpdateTextureArray(stage, index, textureArray); + _encoderStateManager.UpdateTextureArray(stage, binding, textureArray); } } -- 2.47.1 From 5d59c552e734f3183f1aaf676869a30eac59d931 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 14:23:56 +0100 Subject: [PATCH 323/368] Fix Non-Float Textures + Image Read + FSI Buffers Fixes Mario Party Superstars --- .../CodeGen/Msl/Declarations.cs | 6 +++--- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 3 +-- src/Ryujinx.Graphics.Shader/SamplerType.cs | 11 +++++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index e05c30282..ed423a60b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -222,7 +222,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl BufferDefinition buffer = buffers[i]; var needsPadding = buffer.Layout == BufferLayout.Std140; - string fsiSuffix = constant && fsi ? " [[raster_order_group(0)]]" : ""; + string fsiSuffix = !constant && fsi ? " [[raster_order_group(0)]]" : ""; bufferDec[i] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};"; @@ -285,7 +285,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { if (texture.Type != SamplerType.None) { - var textureTypeName = texture.Type.ToMslTextureType(); + var textureTypeName = texture.Type.ToMslTextureType(texture.Format.GetComponentType()); if (texture.ArrayLength > 1) { @@ -329,7 +329,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { TextureDefinition image = images[i]; - var imageTypeName = image.Type.ToMslTextureType(true); + var imageTypeName = image.Type.ToMslTextureType(image.Format.GetComponentType(), true); if (image.ArrayLength > 1) { imageTypeName = $"array<{imageTypeName}, {image.ArrayLength}>"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index f6fa7aa73..da8deb706 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -251,10 +251,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions _ => string.Empty, }; - texCallBuilder.Append($"{prefix}4({string.Join(", ", cElems)})"); + texCallBuilder.Append($"{prefix}4({string.Join(", ", cElems)}), "); } - texCallBuilder.Append(", "); texCallBuilder.Append(coordsBuilder); if (texOp.Inst == Instruction.ImageAtomic) diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index 49c5222e4..18285cd70 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader return typeName; } - public static string ToMslTextureType(this SamplerType type, bool image = false) + public static string ToMslTextureType(this SamplerType type, AggregateType aggregateType, bool image = false) { string typeName; @@ -192,7 +192,14 @@ namespace Ryujinx.Graphics.Shader typeName += "_array"; } - return $"{typeName}"; + var format = aggregateType switch + { + AggregateType.S32 => "int", + AggregateType.U32 => "uint", + _ => "float" + }; + + return $"{typeName}<{format}{(image ? ", access::read_write" : "")}>"; } } } -- 2.47.1 From 6f24b88e88dff2f0bd388687efa0a6dcacdfe353 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 14:39:11 +0100 Subject: [PATCH 324/368] Fix image atomics --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index da8deb706..d88464712 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -255,19 +255,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } texCallBuilder.Append(coordsBuilder); + texCallBuilder.Append(')'); - if (texOp.Inst == Instruction.ImageAtomic) + if (texOp.Inst == Instruction.ImageLoad) { - // TODO: Finish atomic stuff - } - else - { - texCallBuilder.Append(')'); - - if (texOp.Inst == Instruction.ImageLoad) - { - texCallBuilder.Append(GetMaskMultiDest(texOp.Index)); - } + texCallBuilder.Append(GetMaskMultiDest(texOp.Index)); } return texCallBuilder.ToString(); -- 2.47.1 From 2e5ae70b9185cf47476872692ea2eb0f5da435fa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 15:51:06 +0100 Subject: [PATCH 325/368] Precise Float Fixes Fixes artifacts in TOTK --- .../CodeGen/Msl/Declarations.cs | 5 +++++ .../CodeGen/Msl/HelperFunctions/Precise.metal | 14 ++++++++++++++ .../CodeGen/Msl/Instructions/InstGen.cs | 12 ++++++++++++ .../CodeGen/Msl/NumberFormatter.cs | 6 ++++-- .../Ryujinx.Graphics.Shader.csproj | 1 + .../StructuredIr/HelperFunctionsMask.cs | 2 ++ .../StructuredIr/StructuredProgram.cs | 3 ++- .../StructuredIr/StructuredProgramContext.cs | 3 ++- .../StructuredIr/StructuredProgramInfo.cs | 7 ++++++- .../Translation/FeatureFlags.cs | 1 + .../Translation/Transforms/ForcePreciseEnable.cs | 2 ++ .../Translation/TranslatorContext.cs | 1 + 12 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index ed423a60b..50cce8200 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -122,6 +122,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal"); } + if ((info.HelperFunctionsMask & HelperFunctionsMask.Precise) != 0) + { + AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal"); + } + return sets; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal new file mode 100644 index 000000000..366bea1ac --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal @@ -0,0 +1,14 @@ +template +[[clang::optnone]] T PreciseFAdd(T l, T r) { + return fma(T(1), l, r); +} + +template +[[clang::optnone]] T PreciseFSub(T l, T r) { + return fma(T(-1), r, l); +} + +template +[[clang::optnone]] T PreciseFMul(T l, T r) { + return fma(l, r, T(0)); +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index ac294d960..715688987 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -118,6 +118,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions return op + expr[0]; case 2: + if (operation.ForcePrecise) + { + var func = (inst & Instruction.Mask) switch + { + Instruction.Add => "PreciseFAdd", + Instruction.Subtract => "PreciseFSub", + Instruction.Multiply => "PreciseFMul", + }; + + return $"{func}({expr[0]}, {expr[1]})"; + } + return $"{expr[0]} {op} {expr[1]}"; case 3: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs index 8d288da3e..86cdfc0e6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs @@ -49,9 +49,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return false; } - formatted = value.ToString("F9", CultureInfo.InvariantCulture); + formatted = value.ToString("G9", CultureInfo.InvariantCulture); - if (!formatted.Contains('.')) + if (!(formatted.Contains('.') || + formatted.Contains('e') || + formatted.Contains('E'))) { formatted += ".0f"; } diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index f702aae9b..8b05d8829 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -20,5 +20,6 @@ + diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs index 8e7bbd6f1..b70def78c 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs @@ -14,5 +14,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr SwizzleAdd = 1 << 10, FSI = 1 << 11, + + Precise = 1 << 13 } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 394099902..a1aef7f97 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -18,9 +18,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr ShaderDefinitions definitions, ResourceManager resourceManager, TargetLanguage targetLanguage, + bool precise, bool debugMode) { - StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode); + StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, precise, debugMode); for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index 045662a1e..c26086c72 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -36,9 +36,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AttributeUsage attributeUsage, ShaderDefinitions definitions, ResourceManager resourceManager, + bool precise, bool debugMode) { - Info = new StructuredProgramInfo(); + Info = new StructuredProgramInfo(precise); Definitions = definitions; ResourceManager = resourceManager; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index ded2f2a89..585497ed3 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -10,11 +10,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public HelperFunctionsMask HelperFunctionsMask { get; set; } - public StructuredProgramInfo() + public StructuredProgramInfo(bool precise) { Functions = new List(); IoDefinitions = new HashSet(); + + if (precise) + { + HelperFunctionsMask |= HelperFunctionsMask.Precise; + } } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index 82a54db83..26c924e89 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -26,5 +26,6 @@ namespace Ryujinx.Graphics.Shader.Translation SharedMemory = 1 << 11, Store = 1 << 12, VtgAsCompute = 1 << 13, + Precise = 1 << 14, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs index 6b7e1410f..c774816a3 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs @@ -27,6 +27,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms addOp.Inst == (Instruction.FP32 | Instruction.Add) && addOp.GetSource(1).Type == OperandType.Constant) { + context.UsedFeatures |= FeatureFlags.Precise; + addOp.ForcePrecise = true; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 5ca17690e..bec20bc2c 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -332,6 +332,7 @@ namespace Ryujinx.Graphics.Shader.Translation definitions, resourceManager, Options.TargetLanguage, + usedFeatures.HasFlag(FeatureFlags.Precise), Options.Flags.HasFlag(TranslationFlags.DebugMode)); int geometryVerticesPerPrimitive = Definitions.OutputTopology switch -- 2.47.1 From 2a7375209ec9fa9412aff7f9c9ab866c83e38a7e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 18:13:49 +0100 Subject: [PATCH 326/368] Finally fix (most) image atomics --- src/Ryujinx.Graphics.Metal/Program.cs | 6 ++- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 43 ++++++++++++++++--- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index c1eae2273..c76930321 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -46,7 +46,11 @@ namespace Ryujinx.Graphics.Metal { ShaderSource shader = _shaders[i]; - var compileOptions = new MTLCompileOptions { PreserveInvariance = true }; + var compileOptions = new MTLCompileOptions + { + PreserveInvariance = true, + LanguageVersion = MTLLanguageVersion.Version31, + }; var index = i; _handles[i] = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, (library, error) => CompilationResultHandler(library, error, index)); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index d88464712..31f904787 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -211,7 +211,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } else { - coordsBuilder.Append(Src(AggregateType.S32)); + coordsBuilder.Append($"uint({Src(AggregateType.S32)})"); } if (isArray) @@ -251,15 +251,44 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions _ => string.Empty, }; - texCallBuilder.Append($"{prefix}4({string.Join(", ", cElems)}), "); + texCallBuilder.Append($"{prefix}4({string.Join(", ", cElems)})"); + texCallBuilder.Append(", "); + texCallBuilder.Append(coordsBuilder); } - texCallBuilder.Append(coordsBuilder); - texCallBuilder.Append(')'); - - if (texOp.Inst == Instruction.ImageLoad) + if (texOp.Inst == Instruction.ImageAtomic) { - texCallBuilder.Append(GetMaskMultiDest(texOp.Index)); + // Atomics do (coord, value) + texCallBuilder.Append(coordsBuilder); + texCallBuilder.Append(", "); + + AggregateType type = texOp.Format.GetComponentType(); + + if ((texOp.Flags & TextureFlags.AtomicMask) == TextureFlags.CAS) + { + texCallBuilder.Append(Src(type)); // Compare value. + } + + string value = (texOp.Flags & TextureFlags.AtomicMask) switch + { + TextureFlags.Increment => NumberFormatter.FormatInt(1, type), // TODO: Clamp value + TextureFlags.Decrement => NumberFormatter.FormatInt(-1, type), // TODO: Clamp value + _ => Src(type), + }; + + texCallBuilder.Append(value); + // This doesn't match what the MSL spec document says so either + // it is wrong or the MSL compiler has a bug. + texCallBuilder.Append(")[0]"); + } + else + { + texCallBuilder.Append(')'); + + if (texOp.Inst == Instruction.ImageLoad) + { + texCallBuilder.Append(GetMaskMultiDest(texOp.Index)); + } } return texCallBuilder.ToString(); -- 2.47.1 From d7072736710fd1b3ad219f76efbc11eed8238416 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 18:40:34 +0100 Subject: [PATCH 327/368] Fix non atomic image loads again --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 31f904787..005f5eafc 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -253,13 +253,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCallBuilder.Append($"{prefix}4({string.Join(", ", cElems)})"); texCallBuilder.Append(", "); - texCallBuilder.Append(coordsBuilder); } + texCallBuilder.Append(coordsBuilder); + if (texOp.Inst == Instruction.ImageAtomic) { - // Atomics do (coord, value) - texCallBuilder.Append(coordsBuilder); texCallBuilder.Append(", "); AggregateType type = texOp.Format.GetComponentType(); -- 2.47.1 From 7eaaeea9992b086eee1d23a8536f062c2d3293b4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 3 Aug 2024 17:08:50 +0100 Subject: [PATCH 328/368] Format --- .../Threed/ComputeDraw/VtgAsComputeContext.cs | 2 -- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 14 +++++++------- src/Ryujinx.Graphics.Metal/FormatTable.cs | 2 +- src/Ryujinx.Graphics.Metal/Program.cs | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs index a7840fb03..34f2cfcad 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs @@ -11,8 +11,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw /// class VtgAsComputeContext : IDisposable { - private const int DummyBufferSize = 16; - private readonly GpuContext _context; /// diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 0d3df86c7..55c995760 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -97,12 +97,12 @@ namespace Ryujinx.Graphics.Metal _currentState.ClearLoadAction = clear; } - public void DirtyTextures() + public readonly void DirtyTextures() { SignalDirty(DirtyFlags.Textures); } - public void DirtyImages() + public readonly void DirtyImages() { SignalDirty(DirtyFlags.Images); } @@ -830,7 +830,7 @@ namespace Ryujinx.Graphics.Metal SignalDirty(DirtyFlags.Images); } - public void UpdateTextureArray(ShaderStage stage, int binding, TextureArray array) + public readonly void UpdateTextureArray(ShaderStage stage, int binding, TextureArray array) { ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, binding, ArrayGrowthSize); @@ -842,7 +842,7 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array) + public readonly void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array) { ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayExtraRefs, setIndex - MetalRenderer.TotalSets); @@ -854,7 +854,7 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdateImageArray(ShaderStage stage, int binding, ImageArray array) + public readonly void UpdateImageArray(ShaderStage stage, int binding, ImageArray array) { ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayRefs, binding, ArrayGrowthSize); @@ -866,7 +866,7 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array) + public readonly void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array) { ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex - MetalRenderer.TotalSets); @@ -1338,7 +1338,7 @@ namespace Ryujinx.Graphics.Metal { for (int i = 0; i < count; i++) { - int index = binding + i; + int index = binding + i; ref var image = ref _currentState.ImageRefs[index]; diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index 784ba183a..f6261488b 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.Metal Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8); Add(Format.R8G8B8A8Srgb, MTLPixelFormat.RGBA8UnormsRGB); // Add(Format.R4G4Unorm, MTLPixelFormat.R4G4Unorm); - Add(Format.R4G4B4A4Unorm, MTLPixelFormat.RGBA8Unorm); + Add(Format.R4G4B4A4Unorm, MTLPixelFormat.RGBA8Unorm); // Add(Format.R5G5B5X1Unorm, MTLPixelFormat.R5G5B5X1Unorm); Add(Format.R5G5B5A1Unorm, MTLPixelFormat.BGR5A1Unorm); Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm); diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index c76930321..170f178ad 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -15,8 +15,8 @@ namespace Ryujinx.Graphics.Metal class Program : IProgram { private ProgramLinkStatus _status; - private ShaderSource[] _shaders; - private GCHandle[] _handles; + private readonly ShaderSource[] _shaders; + private readonly GCHandle[] _handles; private int _successCount; public MTLFunction VertexFunction; -- 2.47.1 From ceaa1748594bf74eeca4c353b4b7ea355fff6514 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 5 Aug 2024 13:18:00 +0100 Subject: [PATCH 329/368] Get render command encoder after finalising buffers Fixes crash in Fire Emblem: Houses --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index a4654d36a..8fb407905 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -315,8 +315,6 @@ namespace Ryujinx.Graphics.Metal return; } - var renderCommandEncoder = GetOrCreateRenderEncoder(true); - if (TopologyUnsupported(_encoderStateManager.Topology)) { var pattern = GetIndexBufferPattern(); @@ -326,6 +324,7 @@ namespace Ryujinx.Graphics.Metal var mtlBuffer = buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value; var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); + var renderCommandEncoder = GetOrCreateRenderEncoder(true); renderCommandEncoder.DrawIndexedPrimitives( primitiveType, @@ -337,6 +336,7 @@ namespace Ryujinx.Graphics.Metal else { var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); + var renderCommandEncoder = GetOrCreateRenderEncoder(true); renderCommandEncoder.DrawPrimitives( primitiveType, @@ -449,8 +449,6 @@ namespace Ryujinx.Graphics.Metal public void DrawIndirect(BufferRange indirectBuffer) { - var renderCommandEncoder = GetOrCreateRenderEncoder(true); - if (TopologyUnsupported(_encoderStateManager.Topology)) { // TODO: Reindex unsupported topologies @@ -462,6 +460,7 @@ namespace Ryujinx.Graphics.Metal .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); + var renderCommandEncoder = GetOrCreateRenderEncoder(true); renderCommandEncoder.DrawPrimitives( primitiveType, -- 2.47.1 From e8de1156ebebec81322c86e99a3d6cee0d4fedeb Mon Sep 17 00:00:00 2001 From: riperiperi Date: Mon, 5 Aug 2024 22:58:37 +0100 Subject: [PATCH 330/368] Patch some leaks and only perform copies on valid textures (#37) --- .../CommandBufferEncoder.cs | 2 +- .../EncoderStateManager.cs | 5 +---- src/Ryujinx.Graphics.Metal/Program.cs | 2 +- src/Ryujinx.Graphics.Metal/Sampler.cs | 6 ++++-- src/Ryujinx.Graphics.Metal/Texture.cs | 10 ++++++++++ src/Ryujinx.Graphics.Metal/TextureBase.cs | 17 ++++++++++++----- 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs index 82584629d..26cb4f5c7 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs @@ -149,7 +149,7 @@ class CommandBufferEncoder { EndCurrentPass(); - var descriptor = new MTLBlitPassDescriptor(); + using var descriptor = new MTLBlitPassDescriptor(); var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); CurrentEncoder = blitCommandEncoder; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 55c995760..88731a504 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -176,15 +176,12 @@ namespace Ryujinx.Graphics.Metal public readonly MTLComputeCommandEncoder CreateComputeCommandEncoder() { - var descriptor = new MTLComputePassDescriptor(); + using var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); // Mark all state as dirty to ensure it is set on the encoder SignalDirty(DirtyFlags.ComputeAll); - // Cleanup - descriptor.Dispose(); - return computeCommandEncoder; } diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 170f178ad..11e9be55b 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Metal { ShaderSource shader = _shaders[i]; - var compileOptions = new MTLCompileOptions + using var compileOptions = new MTLCompileOptions { PreserveInvariance = true, LanguageVersion = MTLLanguageVersion.Version31, diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index 7930627d4..1189288f6 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Metal MTLSamplerBorderColor borderColor = GetConstrainedBorderColor(info.BorderColor, out _); - var samplerState = device.NewSamplerState(new MTLSamplerDescriptor + using var descriptor = new MTLSamplerDescriptor { BorderColor = borderColor, MinFilter = minFilter, @@ -31,7 +31,9 @@ namespace Ryujinx.Graphics.Metal TAddressMode = info.AddressV.Convert(), RAddressMode = info.AddressP.Convert(), SupportArgumentBuffers = true - }); + }; + + var samplerState = device.NewSamplerState(descriptor); _mtlSamplerState = samplerState; } diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 5ca92eb1e..4219f3db9 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -151,6 +151,11 @@ namespace Ryujinx.Graphics.Metal TextureBase src = this; TextureBase dst = (TextureBase)destination; + if (!Valid || !dst.Valid) + { + return; + } + var srcImage = GetHandle(); var dstImage = dst.GetHandle(); @@ -203,6 +208,11 @@ namespace Ryujinx.Graphics.Metal TextureBase src = this; TextureBase dst = (TextureBase)destination; + if (!Valid || !dst.Valid) + { + return; + } + var srcImage = GetHandle(); var dstImage = dst.GetHandle(); diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index 964f06e22..fecd64958 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -2,13 +2,16 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System; using System.Runtime.Versioning; +using System.Threading; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] abstract class TextureBase : IDisposable { - private bool _disposed; + private int _isValid = 1; + + public bool Valid => Volatile.Read(ref _isValid) != 0; protected readonly Pipeline Pipeline; protected readonly MTLDevice Device; @@ -35,7 +38,7 @@ namespace Ryujinx.Graphics.Metal public MTLTexture GetHandle() { - if (_disposed) + if (_isValid == 0) { return new MTLTexture(IntPtr.Zero); } @@ -50,11 +53,15 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { - if (MtlTexture != IntPtr.Zero) + bool wasValid = Interlocked.Exchange(ref _isValid, 0) != 0; + + if (wasValid) { - MtlTexture.Dispose(); + if (MtlTexture != IntPtr.Zero) + { + MtlTexture.Dispose(); + } } - _disposed = true; } } } -- 2.47.1 From 782299c12387b204e8b9c5080748a68bec7554d9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 5 Aug 2024 23:31:24 +0100 Subject: [PATCH 331/368] Metal: Argument Buffer Pre-Pass (#38) * Init * Fix missing flags * Cleanup --- .../CommandBufferEncoder.cs | 6 +- .../EncoderResources.cs | 63 +++++++ src/Ryujinx.Graphics.Metal/EncoderState.cs | 3 + .../EncoderStateManager.cs | 154 +++++++++++------- src/Ryujinx.Graphics.Metal/Pipeline.cs | 22 +++ 5 files changed, 187 insertions(+), 61 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/EncoderResources.cs diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs index 26cb4f5c7..ec4150030 100644 --- a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs +++ b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs @@ -18,11 +18,11 @@ class CommandBufferEncoder { public EncoderType CurrentEncoderType { get; private set; } = EncoderType.None; - public MTLBlitCommandEncoder BlitEncoder => new MTLBlitCommandEncoder(CurrentEncoder.Value); + public MTLBlitCommandEncoder BlitEncoder => new(CurrentEncoder.Value); - public MTLComputeCommandEncoder ComputeEncoder => new MTLComputeCommandEncoder(CurrentEncoder.Value); + public MTLComputeCommandEncoder ComputeEncoder => new(CurrentEncoder.Value); - public MTLRenderCommandEncoder RenderEncoder => new MTLRenderCommandEncoder(CurrentEncoder.Value); + public MTLRenderCommandEncoder RenderEncoder => new(CurrentEncoder.Value); internal MTLCommandEncoder? CurrentEncoder { get; private set; } diff --git a/src/Ryujinx.Graphics.Metal/EncoderResources.cs b/src/Ryujinx.Graphics.Metal/EncoderResources.cs new file mode 100644 index 000000000..4fbb9b282 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/EncoderResources.cs @@ -0,0 +1,63 @@ +using SharpMetal.Metal; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Metal +{ + public struct RenderEncoderResources + { + public List Resources = new(); + public List VertexBuffers = new(); + public List FragmentBuffers = new(); + + public RenderEncoderResources() { } + + public void Clear() + { + Resources.Clear(); + VertexBuffers.Clear(); + FragmentBuffers.Clear(); + } + } + + public struct ComputeEncoderResources + { + public List Resources = new(); + public List Buffers = new(); + + public ComputeEncoderResources() { } + + public void Clear() + { + Resources.Clear(); + Buffers.Clear(); + } + } + + public struct BufferResource + { + public MTLBuffer Buffer; + public ulong Offset; + public ulong Binding; + + public BufferResource(MTLBuffer buffer, ulong offset, ulong binding) + { + Buffer = buffer; + Offset = offset; + Binding = binding; + } + } + + public struct Resource + { + public MTLResource MtlResource; + public MTLResourceUsage ResourceUsage; + public MTLRenderStages Stages; + + public Resource(MTLResource resource, MTLResourceUsage resourceUsage, MTLRenderStages stages) + { + MtlResource = resource; + ResourceUsage = resourceUsage; + Stages = stages; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index a99a6f35a..d5dd5123b 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -152,6 +152,9 @@ namespace Ryujinx.Graphics.Metal // Only to be used for present public bool ClearLoadAction = false; + public RenderEncoderResources RenderEncoderResources = new(); + public ComputeEncoderResources ComputeEncoderResources = new(); + public EncoderState() { Pipeline.Initialize(); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 88731a504..e50438a10 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -62,6 +62,16 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= flags; } + public void SignalRenderDirty() + { + SignalDirty(DirtyFlags.RenderAll); + } + + public void SignalComputeDirty() + { + SignalDirty(DirtyFlags.ComputeAll); + } + public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All) { _currentState = state ?? _mainState; @@ -110,7 +120,7 @@ namespace Ryujinx.Graphics.Metal public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() { // Initialise Pass & State - var renderPassDescriptor = new MTLRenderPassDescriptor(); + using var renderPassDescriptor = new MTLRenderPassDescriptor(); for (int i = 0; i < Constants.MaxColorAttachments; i++) { @@ -165,12 +175,6 @@ namespace Ryujinx.Graphics.Metal // Initialise Encoder var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); - // Mark all state as dirty to ensure it is set on the encoder - SignalDirty(DirtyFlags.RenderAll); - - // Cleanup - renderPassDescriptor.Dispose(); - return renderCommandEncoder; } @@ -179,18 +183,69 @@ namespace Ryujinx.Graphics.Metal using var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); - // Mark all state as dirty to ensure it is set on the encoder - SignalDirty(DirtyFlags.ComputeAll); - return computeCommandEncoder; } + public void RenderResourcesPrepass() + { + _currentState.RenderEncoderResources.Clear(); + + if ((_currentState.Dirty & DirtyFlags.RenderPipeline) != 0) + { + SetVertexBuffers(_currentState.VertexBuffers, ref _currentState.RenderEncoderResources); + } + + if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) + { + UpdateAndBind(_currentState.RenderProgram, Constants.ConstantBuffersSetIndex, ref _currentState.RenderEncoderResources); + } + + if ((_currentState.Dirty & DirtyFlags.Storages) != 0) + { + UpdateAndBind(_currentState.RenderProgram, Constants.StorageBuffersSetIndex, ref _currentState.RenderEncoderResources); + } + + if ((_currentState.Dirty & DirtyFlags.Textures) != 0) + { + UpdateAndBind(_currentState.RenderProgram, Constants.TexturesSetIndex, ref _currentState.RenderEncoderResources); + } + + if ((_currentState.Dirty & DirtyFlags.Images) != 0) + { + UpdateAndBind(_currentState.RenderProgram, Constants.ImagesSetIndex, ref _currentState.RenderEncoderResources); + } + } + + public void ComputeResourcesPrepass() + { + _currentState.ComputeEncoderResources.Clear(); + + if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) + { + UpdateAndBind(_currentState.ComputeProgram, Constants.ConstantBuffersSetIndex, ref _currentState.ComputeEncoderResources); + } + + if ((_currentState.Dirty & DirtyFlags.Storages) != 0) + { + UpdateAndBind(_currentState.ComputeProgram, Constants.StorageBuffersSetIndex, ref _currentState.ComputeEncoderResources); + } + + if ((_currentState.Dirty & DirtyFlags.Textures) != 0) + { + UpdateAndBind(_currentState.ComputeProgram, Constants.TexturesSetIndex, ref _currentState.ComputeEncoderResources); + } + + if ((_currentState.Dirty & DirtyFlags.Images) != 0) + { + UpdateAndBind(_currentState.ComputeProgram, Constants.ImagesSetIndex, ref _currentState.ComputeEncoderResources); + } + } + public void RebindRenderState(MTLRenderCommandEncoder renderCommandEncoder) { if ((_currentState.Dirty & DirtyFlags.RenderPipeline) != 0) { SetRenderPipelineState(renderCommandEncoder); - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); } if ((_currentState.Dirty & DirtyFlags.DepthStencil) != 0) @@ -233,24 +288,19 @@ namespace Ryujinx.Graphics.Metal SetScissors(renderCommandEncoder); } - if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) + foreach (var resource in _currentState.RenderEncoderResources.Resources) { - UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.ConstantBuffersSetIndex); + renderCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage, resource.Stages); } - if ((_currentState.Dirty & DirtyFlags.Storages) != 0) + foreach (var buffer in _currentState.RenderEncoderResources.VertexBuffers) { - UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.StorageBuffersSetIndex); + renderCommandEncoder.SetVertexBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); } - if ((_currentState.Dirty & DirtyFlags.Textures) != 0) + foreach (var buffer in _currentState.RenderEncoderResources.FragmentBuffers) { - UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.TexturesSetIndex); - } - - if ((_currentState.Dirty & DirtyFlags.Images) != 0) - { - UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.ImagesSetIndex); + renderCommandEncoder.SetFragmentBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); } _currentState.Dirty &= ~DirtyFlags.RenderAll; @@ -263,24 +313,14 @@ namespace Ryujinx.Graphics.Metal SetComputePipelineState(computeCommandEncoder); } - if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) + foreach (var resource in _currentState.ComputeEncoderResources.Resources) { - UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.ConstantBuffersSetIndex); + computeCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage); } - if ((_currentState.Dirty & DirtyFlags.Storages) != 0) + foreach (var buffer in _currentState.ComputeEncoderResources.Buffers) { - UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.StorageBuffersSetIndex); - } - - if ((_currentState.Dirty & DirtyFlags.Textures) != 0) - { - UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.TexturesSetIndex); - } - - if ((_currentState.Dirty & DirtyFlags.Images) != 0) - { - UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.ImagesSetIndex); + computeCommandEncoder.SetBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); } _currentState.Dirty &= ~DirtyFlags.ComputeAll; @@ -1013,7 +1053,7 @@ namespace Ryujinx.Graphics.Metal pipeline.VertexBindingDescriptionsCount = Constants.ZeroBufferIndex + 1; // TODO: move this out? } - private readonly void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferState[] bufferStates) + private readonly void SetVertexBuffers(VertexBufferState[] bufferStates, ref readonly RenderEncoderResources resources) { for (int i = 0; i < bufferStates.Length; i++) { @@ -1021,7 +1061,7 @@ namespace Ryujinx.Graphics.Metal if (mtlBuffer.NativePtr != IntPtr.Zero) { - renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)i); + resources.VertexBuffers.Add(new BufferResource(mtlBuffer, (ulong)offset, (ulong)i)); } } @@ -1035,10 +1075,10 @@ namespace Ryujinx.Graphics.Metal } var zeroMtlBuffer = autoZeroBuffer.Get(_pipeline.Cbs).Value; - renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex); + resources.VertexBuffers.Add(new BufferResource(zeroMtlBuffer, 0, Constants.ZeroBufferIndex)); } - private readonly void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, uint setIndex) + private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly RenderEncoderResources resources) { var bindingSegments = program.BindingSegments[setIndex]; @@ -1120,7 +1160,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); + resources.Resources.Add(new Resource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages)); } break; case Constants.StorageBuffersSetIndex: @@ -1170,7 +1210,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); + resources.Resources.Add(new Resource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages)); } break; case Constants.TexturesSetIndex: @@ -1226,7 +1266,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages); + resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages)); } } else @@ -1268,8 +1308,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), - MTLResourceUsage.Read, renderStages); + resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages)); } foreach (var sampler in samplers) @@ -1325,7 +1364,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages); + resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages)); } } } @@ -1364,7 +1403,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages); + resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); } } break; @@ -1375,18 +1414,18 @@ namespace Ryujinx.Graphics.Metal { vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds)); var mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - renderCommandEncoder.SetVertexBuffer(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + resources.VertexBuffers.Add(new BufferResource(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); } if (program.FragArgumentBufferSizes[setIndex] > 0) { fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds)); var mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - renderCommandEncoder.SetFragmentBuffer(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + resources.FragmentBuffers.Add(new BufferResource(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); } } - private readonly void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, uint setIndex) + private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly ComputeEncoderResources resources) { var bindingSegments = program.BindingSegments[setIndex]; @@ -1443,7 +1482,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read); + resources.Resources.Add(new Resource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, 0)); resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; resourceIdIndex++; } @@ -1480,7 +1519,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); + resources.Resources.Add(new Resource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; resourceIdIndex++; } @@ -1511,7 +1550,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); + resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, 0)); resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; resourceIdIndex++; @@ -1545,8 +1584,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), - MTLResourceUsage.Read); + resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, 0)); resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; resourceIdIndex++; @@ -1580,7 +1618,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); + resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, 0)); resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; resourceIdIndex++; } @@ -1610,7 +1648,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); + resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; resourceIdIndex++; } @@ -1625,7 +1663,7 @@ namespace Ryujinx.Graphics.Metal { argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); var mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value; - computeCommandEncoder.SetBuffer(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + resources.Buffers.Add(new BufferResource(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); } } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 8fb407905..87306abcf 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -82,6 +82,17 @@ namespace Ryujinx.Graphics.Metal public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false) { + // Mark all state as dirty to ensure it is set on the new encoder + if (Cbs.Encoders.CurrentEncoderType != EncoderType.Render) + { + _encoderStateManager.SignalRenderDirty(); + } + + if (forDraw) + { + _encoderStateManager.RenderResourcesPrepass(); + } + MTLRenderCommandEncoder renderCommandEncoder = Cbs.Encoders.EnsureRenderEncoder(); if (forDraw) @@ -99,6 +110,17 @@ namespace Ryujinx.Graphics.Metal public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false) { + // Mark all state as dirty to ensure it is set on the new encoder + if (Cbs.Encoders.CurrentEncoderType != EncoderType.Compute) + { + _encoderStateManager.SignalComputeDirty(); + } + + if (forDispatch) + { + _encoderStateManager.ComputeResourcesPrepass(); + } + MTLComputeCommandEncoder computeCommandEncoder = Cbs.Encoders.EnsureComputeEncoder(); if (forDispatch) -- 2.47.1 From 4a11cc9c7a8188a09c9bfd3c1522cd68fe2b76a9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 10 Aug 2024 21:07:20 +0100 Subject: [PATCH 332/368] Rasterizer Discard + Multisample State --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 15 +++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 ++-- src/Ryujinx.Graphics.Metal/State/PipelineState.cs | 9 +++------ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e50438a10..a417f950a 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -399,6 +399,13 @@ namespace Ryujinx.Graphics.Metal } } + public readonly void UpdateRasterizerDiscard(bool discard) + { + _currentState.Pipeline.RasterizerDiscardEnable = discard; + + SignalDirty(DirtyFlags.RenderPipeline); + } + public readonly void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) { _currentState.FramebufferUsingColorWriteMask = false; @@ -656,6 +663,14 @@ namespace Ryujinx.Graphics.Metal SignalDirty(DirtyFlags.DepthBias); } + public readonly void UpdateMultisampleState(MultisampleDescriptor multisample) + { + _currentState.Pipeline.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable; + _currentState.Pipeline.AlphaToOneEnable = multisample.AlphaToOneEnable; + + SignalDirty(DirtyFlags.RenderPipeline); + } + public void UpdateScissors(ReadOnlySpan> regions) { for (int i = 0; i < regions.Length; i++) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 87306abcf..70a6e7c2d 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -597,7 +597,7 @@ namespace Ryujinx.Graphics.Metal public void SetMultisampleState(MultisampleDescriptor multisample) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _encoderStateManager.UpdateMultisampleState(multisample); } public void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel) @@ -638,7 +638,7 @@ namespace Ryujinx.Graphics.Metal public void SetRasterizerDiscard(bool discard) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _encoderStateManager.UpdateRasterizerDiscard(discard); } public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs index fa6d5410b..425b9a977 100644 --- a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs +++ b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs @@ -208,12 +208,9 @@ namespace Ryujinx.Graphics.Metal } } - /* TODO: enable when sharpmetal fixes the bindings - renderPipelineDescriptor.AlphaToCoverageEnabled = AlphaToCoverageEnable; - renderPipelineDescriptor.AlphaToOneEnabled = AlphaToOneEnable; - renderPipelineDescriptor.RasterizationEnabled = !RasterizerDiscardEnable; - */ - + renderPipelineDescriptor.SetAlphaToCoverageEnabled(AlphaToCoverageEnable); + renderPipelineDescriptor.SetAlphaToOneEnabled(AlphaToOneEnable); + renderPipelineDescriptor.SetRasterizationEnabled(!RasterizerDiscardEnable); renderPipelineDescriptor.SampleCount = Math.Max(1, SamplesCount); var vertexDescriptor = BuildVertexDescriptor(); -- 2.47.1 From 1fc96128faca3a142fd2a59a57b27bcc60c72558 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 11 Aug 2024 20:29:08 +0100 Subject: [PATCH 333/368] IaIndexing Fixes shader problems in Donkey Kong Country Tropical Freeze, and Fire Emblem: Three Houses --- src/Ryujinx.Graphics.Metal/Program.cs | 4 +- .../CodeGen/Msl/CodeGenContext.cs | 4 +- .../CodeGen/Msl/Declarations.cs | 311 ++++++++++-------- 3 files changed, 175 insertions(+), 144 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 11e9be55b..c3e5a8d7c 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -76,7 +76,7 @@ namespace Ryujinx.Graphics.Metal return; } - switch (_shaders[index].Stage) + switch (shader.Stage) { case ShaderStage.Compute: ComputeFunction = library.NewFunction(StringHelper.NSString("kernelMain")); @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Metal FragmentFunction = library.NewFunction(StringHelper.NSString("fragmentMain")); break; default: - Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {_shaders[index].Stage}!"); + Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shader.Stage}!"); break; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index 79c13964c..2fa188ea9 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -60,9 +60,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return _sb.ToString(); } - public void EnterScope() + public void EnterScope(string prefix = "") { - AppendLine("{"); + AppendLine(prefix + "{"); _level++; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 50cce8200..1832ea146 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -139,6 +139,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { if (isMainFunc) { + // TODO: Support OaIndexing + if (context.Definitions.IaIndexing) + { + context.EnterScope($"array {Defaults.IAttributePrefix} = "); + + for (int i = 0; i < Constants.MaxAttributes; i++) + { + context.AppendLine($"in.{Defaults.IAttributePrefix}{i},"); + } + + context.LeaveScope(";"); + } + DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); DeclareMemories(context, context.Properties.SharedMemories.Values, isShared: true); @@ -356,171 +369,189 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) { + if (context.Definitions.Stage == ShaderStage.Compute) + { + return; + } + + switch (context.Definitions.Stage) + { + case ShaderStage.Vertex: + context.AppendLine("struct VertexIn"); + break; + case ShaderStage.Fragment: + context.AppendLine("struct FragmentIn"); + break; + } + + context.EnterScope(); + + if (context.Definitions.Stage == ShaderStage.Fragment) + { + // TODO: check if it's needed + context.AppendLine("float4 position [[position, invariant]];"); + context.AppendLine("bool front_facing [[front_facing]];"); + context.AppendLine("float2 point_coord [[point_coord]];"); + } + if (context.Definitions.IaIndexing) { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Unhandled IA Indexing!"); - } - else - { - if (inputs.Any() || context.Definitions.Stage != ShaderStage.Compute) + // MSL does not support arrays in stage I/O + // We need to use the SPIRV-Cross workaround + for (int i = 0; i < Constants.MaxAttributes; i++) { - string prefix = ""; + var suffix = context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{i})]]" : $"[[attribute({i})]]"; + context.AppendLine($"float4 {Defaults.IAttributePrefix}{i} {suffix};"); + } + } - switch (context.Definitions.Stage) + if (inputs.Any()) + { + foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) + { + if (context.Definitions.IaIndexing && ioDefinition.IoVariable == IoVariable.UserDefined) { - case ShaderStage.Vertex: - context.AppendLine($"struct VertexIn"); - break; - case ShaderStage.Fragment: - context.AppendLine($"struct FragmentIn"); - break; + continue; } - context.EnterScope(); + string iq = string.Empty; if (context.Definitions.Stage == ShaderStage.Fragment) { - // TODO: check if it's needed - context.AppendLine("float4 position [[position, invariant]];"); - context.AppendLine("bool front_facing [[front_facing]];"); - context.AppendLine("float2 point_coord [[point_coord]];"); + iq = context.Definitions.ImapTypes[ioDefinition.Location].GetFirstUsedType() switch + { + PixelImap.Constant => "[[flat]] ", + PixelImap.ScreenLinear => "[[center_no_perspective]] ", + _ => string.Empty, + }; } - foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) + string type = ioDefinition.IoVariable switch { - string iq = string.Empty; + // IoVariable.Position => "float4", + IoVariable.GlobalId => "uint3", + IoVariable.VertexId => "uint", + IoVariable.VertexIndex => "uint", + // IoVariable.PointCoord => "float2", + _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) + }; + string name = ioDefinition.IoVariable switch + { + // IoVariable.Position => "position", + IoVariable.GlobalId => "global_id", + IoVariable.VertexId => "vertex_id", + IoVariable.VertexIndex => "vertex_index", + // IoVariable.PointCoord => "point_coord", + _ => $"{Defaults.IAttributePrefix}{ioDefinition.Location}" + }; + string suffix = ioDefinition.IoVariable switch + { + // IoVariable.Position => "[[position, invariant]]", + IoVariable.GlobalId => "[[thread_position_in_grid]]", + IoVariable.VertexId => "[[vertex_id]]", + // TODO: Avoid potential redeclaration + IoVariable.VertexIndex => "[[vertex_id]]", + // IoVariable.PointCoord => "[[point_coord]]", + IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]", + _ => "" + }; - if (context.Definitions.Stage == ShaderStage.Fragment) - { - iq = context.Definitions.ImapTypes[ioDefinition.Location].GetFirstUsedType() switch - { - PixelImap.Constant => "[[flat]] ", - PixelImap.ScreenLinear => "[[center_no_perspective]] ", - _ => string.Empty, - }; - } - - string type = ioDefinition.IoVariable switch - { - // IoVariable.Position => "float4", - IoVariable.GlobalId => "uint3", - IoVariable.VertexId => "uint", - IoVariable.VertexIndex => "uint", - // IoVariable.PointCoord => "float2", - _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) - }; - string name = ioDefinition.IoVariable switch - { - // IoVariable.Position => "position", - IoVariable.GlobalId => "global_id", - IoVariable.VertexId => "vertex_id", - IoVariable.VertexIndex => "vertex_index", - // IoVariable.PointCoord => "point_coord", - _ => $"{Defaults.IAttributePrefix}{ioDefinition.Location}" - }; - string suffix = ioDefinition.IoVariable switch - { - // IoVariable.Position => "[[position, invariant]]", - IoVariable.GlobalId => "[[thread_position_in_grid]]", - IoVariable.VertexId => "[[vertex_id]]", - // TODO: Avoid potential redeclaration - IoVariable.VertexIndex => "[[vertex_id]]", - // IoVariable.PointCoord => "[[point_coord]]", - IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]", - _ => "" - }; - - context.AppendLine($"{type} {name} {iq}{suffix};"); - } - - context.LeaveScope(";"); + context.AppendLine($"{type} {name} {iq}{suffix};"); } } + + context.LeaveScope(";"); } private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable outputs) { + switch (context.Definitions.Stage) + { + case ShaderStage.Vertex: + context.AppendLine("struct VertexOut"); + break; + case ShaderStage.Fragment: + context.AppendLine("struct FragmentOut"); + break; + case ShaderStage.Compute: + context.AppendLine("struct KernelOut"); + break; + } + + context.EnterScope(); + if (context.Definitions.OaIndexing) { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Unhandled OA Indexing!"); - } - else - { - if (outputs.Any() || context.Definitions.Stage == ShaderStage.Fragment) + // MSL does not support arrays in stage I/O + // We need to use the SPIRV-Cross workaround + for (int i = 0; i < Constants.MaxAttributes; i++) { - string prefix = ""; - - switch (context.Definitions.Stage) - { - case ShaderStage.Vertex: - context.AppendLine($"struct VertexOut"); - break; - case ShaderStage.Fragment: - context.AppendLine($"struct FragmentOut"); - break; - case ShaderStage.Compute: - context.AppendLine($"struct KernelOut"); - break; - } - - context.EnterScope(); - - outputs = outputs.OrderBy(x => x.Location); - - if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) - { - IoDefinition firstOutput = outputs.ElementAtOrDefault(0); - IoDefinition secondOutput = outputs.ElementAtOrDefault(1); - - var type1 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(firstOutput.Location)); - var type2 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(secondOutput.Location)); - - var name1 = $"color{firstOutput.Location}"; - var name2 = $"color{firstOutput.Location + 1}"; - - context.AppendLine($"{type1} {name1} [[color({firstOutput.Location}), index(0)]];"); - context.AppendLine($"{type2} {name2} [[color({firstOutput.Location}), index(1)]];"); - - outputs = outputs.Skip(2); - } - - foreach (var ioDefinition in outputs) - { - string type = ioDefinition.IoVariable switch - { - IoVariable.Position => "float4", - IoVariable.PointSize => "float", - IoVariable.FragmentOutputColor => GetVarTypeName(context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), - IoVariable.FragmentOutputDepth => "float", - IoVariable.ClipDistance => "float", - _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) - }; - string name = ioDefinition.IoVariable switch - { - IoVariable.Position => "position", - IoVariable.PointSize => "point_size", - IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", - IoVariable.FragmentOutputDepth => "depth", - IoVariable.ClipDistance => "clip_distance", - _ => $"{Defaults.OAttributePrefix}{ioDefinition.Location}" - }; - string suffix = ioDefinition.IoVariable switch - { - IoVariable.Position => "[[position, invariant]]", - IoVariable.PointSize => "[[point_size]]", - IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", - IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", - IoVariable.FragmentOutputDepth => "[[depth(any)]]", - IoVariable.ClipDistance => $"[[clip_distance]][{Defaults.TotalClipDistances}]", - _ => "" - }; - - context.AppendLine($"{type} {name} {suffix};"); - } - - context.LeaveScope(";"); + context.AppendLine($"float4 {Defaults.OAttributePrefix}{i} [[user(loc{i})]];"); } } + + if (outputs.Any()) + { + outputs = outputs.OrderBy(x => x.Location); + + if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) + { + IoDefinition firstOutput = outputs.ElementAtOrDefault(0); + IoDefinition secondOutput = outputs.ElementAtOrDefault(1); + + var type1 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(firstOutput.Location)); + var type2 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(secondOutput.Location)); + + var name1 = $"color{firstOutput.Location}"; + var name2 = $"color{firstOutput.Location + 1}"; + + context.AppendLine($"{type1} {name1} [[color({firstOutput.Location}), index(0)]];"); + context.AppendLine($"{type2} {name2} [[color({firstOutput.Location}), index(1)]];"); + + outputs = outputs.Skip(2); + } + + foreach (var ioDefinition in outputs) + { + if (context.Definitions.OaIndexing && ioDefinition.IoVariable == IoVariable.UserDefined) + { + continue; + } + + string type = ioDefinition.IoVariable switch + { + IoVariable.Position => "float4", + IoVariable.PointSize => "float", + IoVariable.FragmentOutputColor => GetVarTypeName(context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), + IoVariable.FragmentOutputDepth => "float", + IoVariable.ClipDistance => "float", + _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) + }; + string name = ioDefinition.IoVariable switch + { + IoVariable.Position => "position", + IoVariable.PointSize => "point_size", + IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", + IoVariable.FragmentOutputDepth => "depth", + IoVariable.ClipDistance => "clip_distance", + _ => $"{Defaults.OAttributePrefix}{ioDefinition.Location}" + }; + string suffix = ioDefinition.IoVariable switch + { + IoVariable.Position => "[[position, invariant]]", + IoVariable.PointSize => "[[point_size]]", + IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", + IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", + IoVariable.FragmentOutputDepth => "[[depth(any)]]", + IoVariable.ClipDistance => $"[[clip_distance]][{Defaults.TotalClipDistances}]", + _ => "" + }; + + context.AppendLine($"{type} {name} {suffix};"); + } + } + + context.LeaveScope(";"); } private static void AppendHelperFunction(CodeGenContext context, string filename) -- 2.47.1 From b988c0bc3d6a7464e8ecb04182b379af64a00bcf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 12 Aug 2024 13:36:56 +0100 Subject: [PATCH 334/368] Debug Groups --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 32 +++++++-- src/Ryujinx.Graphics.Metal/Pipeline.cs | 77 ++++++++++++++++++++++ 2 files changed, 102 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 87d720c5e..d1a4b4705 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -258,18 +258,24 @@ namespace Ryujinx.Graphics.Metal _pipeline.SwapState(null); return; } - else if (src.Info.Target.IsMultisample()) + + var debugGroupName = "Blit Color "; + + if (src.Info.Target.IsMultisample()) { if (dst.Info.Format.IsSint()) { + debugGroupName += "MS Int"; _pipeline.SetProgram(_programColorBlitMsI); } else if (dst.Info.Format.IsUint()) { + debugGroupName += "MS UInt"; _pipeline.SetProgram(_programColorBlitMsU); } else { + debugGroupName += "MS Float"; _pipeline.SetProgram(_programColorBlitMsF); } } @@ -277,14 +283,17 @@ namespace Ryujinx.Graphics.Metal { if (dst.Info.Format.IsSint()) { + debugGroupName += "Int"; _pipeline.SetProgram(_programColorBlitI); } else if (dst.Info.Format.IsUint()) { + debugGroupName += "UInt"; _pipeline.SetProgram(_programColorBlitU); } else { + debugGroupName += "Float"; _pipeline.SetProgram(_programColorBlitF); } } @@ -303,7 +312,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0); + _pipeline.Draw(4, 1, 0, 0, debugGroupName); // Cleanup if (clear) @@ -441,18 +450,22 @@ namespace Ryujinx.Graphics.Metal private void BlitDepthStencilDraw(Texture src, bool isDepth) { + string debugGroupName; + if (isDepth) { + debugGroupName = "Depth Blit"; _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); } else { + debugGroupName = "Stencil Blit"; _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit); _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); } - _pipeline.Draw(4, 1, 0, 0); + _pipeline.Draw(4, 1, 0, 0, debugGroupName); if (isDepth) { @@ -522,7 +535,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetProgram(_programColorBlitF); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0); + _pipeline.Draw(4, 1, 0, 0, "Draw Texture"); _renderer.BufferManager.Delete(bufferHandle); @@ -572,7 +585,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programStrideChange); - _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1); + _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, "Change Stride"); // Restore previous state _pipeline.SwapState(null); @@ -618,16 +631,21 @@ namespace Ryujinx.Graphics.Metal Span componentMasks = stackalloc uint[index + 1]; componentMasks[index] = componentMask; + var debugGroupName = "Clear Color "; + if (format.IsSint()) { + debugGroupName += "Int"; _pipeline.SetProgram(_programsColorClearI[index]); } else if (format.IsUint()) { + debugGroupName += "UInt"; _pipeline.SetProgram(_programsColorClearU[index]); } else { + debugGroupName += "Float"; _pipeline.SetProgram(_programsColorClearF[index]); } @@ -637,7 +655,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetRenderTargetColorMasks(componentMasks); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0); + _pipeline.Draw(4, 1, 0, 0, debugGroupName); // Restore previous state _pipeline.SwapState(null, clearFlags, false); @@ -686,7 +704,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetViewports(viewports); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); - _pipeline.Draw(4, 1, 0, 0); + _pipeline.Draw(4, 1, 0, 0, "Clear Depth Stencil"); // Cleanup _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 70a6e7c2d..9a6bcc052 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -319,18 +319,85 @@ namespace Ryujinx.Graphics.Metal BufferHolder.Copy(Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size); } + public void PushDebugGroup(string name) + { + var encoder = Encoders.CurrentEncoder; + var debugGroupName = StringHelper.NSString(name); + + if (encoder == null) + { + return; + } + + switch (Encoders.CurrentEncoderType) + { + case EncoderType.Render: + encoder.Value.PushDebugGroup(debugGroupName); + break; + case EncoderType.Blit: + encoder.Value.PushDebugGroup(debugGroupName); + break; + case EncoderType.Compute: + encoder.Value.PushDebugGroup(debugGroupName); + break; + } + } + + public void PopDebugGroup() + { + var encoder = Encoders.CurrentEncoder; + + if (encoder == null) + { + return; + } + + switch (Encoders.CurrentEncoderType) + { + case EncoderType.Render: + encoder.Value.PopDebugGroup(); + break; + case EncoderType.Blit: + encoder.Value.PopDebugGroup(); + break; + case EncoderType.Compute: + encoder.Value.PopDebugGroup(); + break; + } + } + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + { + DispatchCompute(groupsX, groupsY, groupsZ, String.Empty); + } + + public void DispatchCompute(int groupsX, int groupsY, int groupsZ, string debugGroupName) { var computeCommandEncoder = GetOrCreateComputeEncoder(true); ComputeSize localSize = _encoderStateManager.ComputeLocalSize; + if (debugGroupName != String.Empty) + { + PushDebugGroup(debugGroupName); + } + computeCommandEncoder.DispatchThreadgroups( new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ }, new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z }); + + if (debugGroupName != String.Empty) + { + PopDebugGroup(); + } } public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + { + Draw(vertexCount, instanceCount, firstVertex, firstInstance, String.Empty); + } + + public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance, string debugGroupName) { if (vertexCount == 0) { @@ -360,12 +427,22 @@ namespace Ryujinx.Graphics.Metal var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); var renderCommandEncoder = GetOrCreateRenderEncoder(true); + if (debugGroupName != String.Empty) + { + PushDebugGroup(debugGroupName); + } + renderCommandEncoder.DrawPrimitives( primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance); + + if (debugGroupName != String.Empty) + { + PopDebugGroup(); + } } } -- 2.47.1 From 058ce78d31acf32d54a4c43423a62915bee7c036 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 12 Aug 2024 14:09:40 +0100 Subject: [PATCH 335/368] Fix array size query --- .../CodeGen/Msl/Instructions/InstGenMemory.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index 005f5eafc..6ccacc1c4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -605,6 +605,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { context.Properties.Textures.TryGetValue(texOp.GetTextureSetAndBinding(), out TextureDefinition definition); bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; + bool isArray = definition.Type.HasFlag(SamplerType.Array); texCallBuilder.Append("get_"); if (texOp.Index == 0) @@ -617,12 +618,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } else { - texCallBuilder.Append("depth"); + if (isArray) + { + texCallBuilder.Append("array_size"); + } + else + { + texCallBuilder.Append("depth"); + } } texCallBuilder.Append('('); - if (hasLod) + if (hasLod && !isArray) { IAstNode lod = operation.GetSource(0); string lodExpr = GetSourceExpr(context, lod, GetSrcVarType(operation.Inst, 0)); -- 2.47.1 From 3258a4bad15e50cfb0ecf98c0629bf474885d21b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 12 Aug 2024 15:54:41 +0100 Subject: [PATCH 336/368] Logic Operations --- Directory.Packages.props | 2 +- .../EncoderStateManager.cs | 8 ++++++ src/Ryujinx.Graphics.Metal/EnumConversion.cs | 25 +++++++++++++++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- .../State/PipelineState.cs | 14 +++++------ 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index c005fe35c..07fc8cc28 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index a417f950a..7687b80df 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -663,6 +663,14 @@ namespace Ryujinx.Graphics.Metal SignalDirty(DirtyFlags.DepthBias); } + public readonly void UpdateLogicOpState(bool enable, LogicalOp op) + { + _currentState.Pipeline.LogicOpEnable = enable; + _currentState.Pipeline.LogicOp = op.Convert(); + + SignalDirty(DirtyFlags.RenderPipeline); + } + public readonly void UpdateMultisampleState(MultisampleDescriptor multisample) { _currentState.Pipeline.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable; diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index 091de7ef8..e498546e8 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; +using System; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -112,6 +113,30 @@ namespace Ryujinx.Graphics.Metal }; } + public static MTLLogicOperation Convert(this LogicalOp op) + { + return op switch + { + LogicalOp.Clear => MTLLogicOperation.Clear, + LogicalOp.And => MTLLogicOperation.And, + LogicalOp.AndReverse => MTLLogicOperation.AndReverse, + LogicalOp.Copy => MTLLogicOperation.Copy, + LogicalOp.AndInverted => MTLLogicOperation.AndInverted, + LogicalOp.Noop => MTLLogicOperation.Noop, + LogicalOp.Xor => MTLLogicOperation.Xor, + LogicalOp.Or => MTLLogicOperation.Or, + LogicalOp.Nor => MTLLogicOperation.Nor, + LogicalOp.Equiv => MTLLogicOperation.Equivalence, + LogicalOp.Invert => MTLLogicOperation.Invert, + LogicalOp.OrReverse => MTLLogicOperation.OrReverse, + LogicalOp.CopyInverted => MTLLogicOperation.CopyInverted, + LogicalOp.OrInverted => MTLLogicOperation.OrInverted, + LogicalOp.Nand => MTLLogicOperation.Nand, + LogicalOp.Set => MTLLogicOperation.Set, + _ => LogInvalidAndReturn(op, nameof(LogicalOp), MTLLogicOperation.And) + }; + } + public static MTLSamplerMinMagFilter Convert(this MagFilter filter) { return filter switch diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 9a6bcc052..6b42578ea 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -669,7 +669,7 @@ namespace Ryujinx.Graphics.Metal public void SetLogicOpState(bool enable, LogicalOp op) { - // Metal does not support logic operations. + _encoderStateManager.UpdateLogicOpState(enable, op); } public void SetMultisampleState(MultisampleDescriptor multisample) diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs index 425b9a977..85e6907bd 100644 --- a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs +++ b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs @@ -45,10 +45,9 @@ namespace Ryujinx.Graphics.Metal } */ - // Reserved for when API is available. - public int LogicOp + public MTLLogicOperation LogicOp { - readonly get => (int)((Internal.Id0 >> 32) & 0xF); + readonly get => (MTLLogicOperation)((Internal.Id0 >> 32) & 0xF); set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32); } @@ -65,7 +64,6 @@ namespace Ryujinx.Graphics.Metal set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFDFFFFFFFFF) | ((value ? 1UL : 0UL) << 37); } - // Reserved for when API is available. public bool LogicOpEnable { readonly get => ((Internal.Id0 >> 38) & 0x1) != 0UL; @@ -208,9 +206,11 @@ namespace Ryujinx.Graphics.Metal } } - renderPipelineDescriptor.SetAlphaToCoverageEnabled(AlphaToCoverageEnable); - renderPipelineDescriptor.SetAlphaToOneEnabled(AlphaToOneEnable); - renderPipelineDescriptor.SetRasterizationEnabled(!RasterizerDiscardEnable); + renderPipelineDescriptor.LogicOperationEnabled = LogicOpEnable; + renderPipelineDescriptor.LogicOperation = LogicOp; + renderPipelineDescriptor.AlphaToCoverageEnabled = AlphaToCoverageEnable; + renderPipelineDescriptor.AlphaToOneEnabled = AlphaToOneEnable; + renderPipelineDescriptor.RasterizationEnabled = !RasterizerDiscardEnable; renderPipelineDescriptor.SampleCount = Math.Max(1, SamplesCount); var vertexDescriptor = BuildVertexDescriptor(); -- 2.47.1 From 967887d050c664928635d9894ac739e02dc8f2b6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 12 Aug 2024 23:22:15 +0100 Subject: [PATCH 337/368] Upstream changes --- src/Ryujinx.Graphics.Metal/EncoderResources.cs | 4 ++-- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 8 ++++---- src/Ryujinx.Graphics.Metal/Texture.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderResources.cs b/src/Ryujinx.Graphics.Metal/EncoderResources.cs index 4fbb9b282..cfda9bcbe 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderResources.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderResources.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Metal public RenderEncoderResources() { } - public void Clear() + public readonly void Clear() { Resources.Clear(); VertexBuffers.Clear(); @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Metal public ComputeEncoderResources() { } - public void Clear() + public readonly void Clear() { Resources.Clear(); Buffers.Clear(); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 7687b80df..9761de50b 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -62,12 +62,12 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= flags; } - public void SignalRenderDirty() + public readonly void SignalRenderDirty() { SignalDirty(DirtyFlags.RenderAll); } - public void SignalComputeDirty() + public readonly void SignalComputeDirty() { SignalDirty(DirtyFlags.ComputeAll); } @@ -186,7 +186,7 @@ namespace Ryujinx.Graphics.Metal return computeCommandEncoder; } - public void RenderResourcesPrepass() + public readonly void RenderResourcesPrepass() { _currentState.RenderEncoderResources.Clear(); @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Metal } } - public void ComputeResourcesPrepass() + public readonly void ComputeResourcesPrepass() { _currentState.ComputeEncoderResources.Clear(); diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 4219f3db9..82e38def1 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -460,7 +460,7 @@ namespace Ryujinx.Graphics.Metal int width = Info.Width; int height = Info.Height; int depth = Info.Depth; - int levels = Info.GetLevelsClamped(); + int levels = Info.Levels; int layers = Info.GetLayers(); bool is3D = Info.Target == Target.Texture3D; -- 2.47.1 From 2d522b16758a371b7d60315c87b2c20b23f7a643 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 13 Aug 2024 17:03:19 +0100 Subject: [PATCH 338/368] D32FS8 to D24S8 Conversion --- src/Ryujinx.Graphics.Metal/FormatConverter.cs | 49 ++++++++++++ src/Ryujinx.Graphics.Metal/FormatTable.cs | 10 ++- src/Ryujinx.Graphics.Metal/HelperShader.cs | 45 +++++++++++ .../Ryujinx.Graphics.Metal.csproj | 1 + .../Shaders/ConvertD32S8ToD24S8.metal | 66 ++++++++++++++++ src/Ryujinx.Graphics.Metal/Texture.cs | 76 ++++++++++++++++--- 6 files changed, 233 insertions(+), 14 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/FormatConverter.cs create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal diff --git a/src/Ryujinx.Graphics.Metal/FormatConverter.cs b/src/Ryujinx.Graphics.Metal/FormatConverter.cs new file mode 100644 index 000000000..e099187b8 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/FormatConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Metal +{ + class FormatConverter + { + public static void ConvertD24S8ToD32FS8(Span output, ReadOnlySpan input) + { + const float UnormToFloat = 1f / 0xffffff; + + Span outputUint = MemoryMarshal.Cast(output); + ReadOnlySpan inputUint = MemoryMarshal.Cast(input); + + int i = 0; + + for (; i < inputUint.Length; i++) + { + uint depthStencil = inputUint[i]; + uint depth = depthStencil >> 8; + uint stencil = depthStencil & 0xff; + + int j = i * 2; + + outputUint[j] = (uint)BitConverter.SingleToInt32Bits(depth * UnormToFloat); + outputUint[j + 1] = stencil; + } + } + + public static void ConvertD32FS8ToD24S8(Span output, ReadOnlySpan input) + { + Span outputUint = MemoryMarshal.Cast(output); + ReadOnlySpan inputUint = MemoryMarshal.Cast(input); + + int i = 0; + + for (; i < inputUint.Length; i += 2) + { + float depth = BitConverter.Int32BitsToSingle((int)inputUint[i]); + uint stencil = inputUint[i + 1]; + uint depthStencil = (Math.Clamp((uint)(depth * 0xffffff), 0, 0xffffff) << 8) | (stencil & 0xff); + + int j = i >> 1; + + outputUint[j] = depthStencil; + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs index f6261488b..c1f8923f9 100644 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ b/src/Ryujinx.Graphics.Metal/FormatTable.cs @@ -172,21 +172,25 @@ namespace Ryujinx.Graphics.Metal { var mtlFormat = _table[(int)format]; - if (mtlFormat == MTLPixelFormat.Depth24UnormStencil8) + if (IsD24S8(format)) { if (!MTLDevice.CreateSystemDefaultDevice().Depth24Stencil8PixelFormatSupported) { - Logger.Error?.PrintMsg(LogClass.Gpu, "Application requested Depth24Stencil8, which is unsupported on this device!"); mtlFormat = MTLPixelFormat.Depth32FloatStencil8; } } if (mtlFormat == MTLPixelFormat.Invalid) { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Application requested {format}, no direct equivalent was found!"); + Logger.Error?.PrintMsg(LogClass.Gpu, $"Format {format} is not supported by the host."); } return mtlFormat; } + + public static bool IsD24S8(Format format) + { + return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm || format == Format.X8UintD24Unorm; + } } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index d1a4b4705..8039641ea 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Metal private readonly List _programsColorClearU = new(); private readonly IProgram _programDepthStencilClear; private readonly IProgram _programStrideChange; + private readonly IProgram _programConvertD32S8ToD24S8; private readonly IProgram _programDepthBlit; private readonly IProgram _programDepthBlitMs; private readonly IProgram _programStencilBlit; @@ -151,6 +152,17 @@ namespace Ryujinx.Graphics.Metal new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) ], strideChangeResourceLayout, device, new ComputeSize(64, 1, 1)); + var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); + + var convertD32S8ToD24S8Source = ReadMsl("ConvertD32S8ToD24S8.metal"); + _programConvertD32S8ToD24S8 = new Program( + [ + new ShaderSource(convertD32S8ToD24S8Source, ShaderStage.Compute, TargetLanguage.Msl) + ], convertD32S8ToD24S8ResourceLayout, device, new ComputeSize(64, 1, 1)); + var depthBlitSource = ReadMsl("DepthBlit.metal"); _programDepthBlit = new Program( [ @@ -591,6 +603,39 @@ namespace Ryujinx.Graphics.Metal _pipeline.SwapState(null); } + public unsafe void ConvertD32S8ToD24S8(CommandBufferScoped cbs, BufferHolder src, Auto dstBuffer, int pixelCount, int dstOffset) + { + int inSize = pixelCount * 2 * sizeof(int); + + var srcBuffer = src.GetBuffer(); + + const int ParamsBufferSize = sizeof(int) * 2; + + // Save current state + _pipeline.SwapState(_helperShaderState); + + Span shaderParams = stackalloc int[2]; + + shaderParams[0] = pixelCount; + shaderParams[1] = dstOffset; + + using var buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); + buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); + _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); + + Span> sbRanges = new Auto[2]; + + sbRanges[0] = srcBuffer; + sbRanges[1] = dstBuffer; + _pipeline.SetStorageBuffers(1, sbRanges); + + _pipeline.SetProgram(_programConvertD32S8ToD24S8); + _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1, "D32S8 to D24S8 Conversion"); + + // Restore previous state + _pipeline.SwapState(null); + } + public unsafe void ClearColor( int index, ReadOnlySpan clearColor, diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 8cbdc8bcb..0839c426a 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -18,6 +18,7 @@ + diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal b/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal new file mode 100644 index 000000000..789527cbb --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal @@ -0,0 +1,66 @@ +#include + +using namespace metal; + +struct StrideArguments { + int pixelCount; + int dstStartOffset; +}; + +struct InData { + uint data[1]; +}; + +struct OutData { + uint data[1]; +}; + +struct ConstantBuffers { + constant StrideArguments* stride_arguments; +}; + +struct StorageBuffers { + device InData* in_data; + device OutData* out_data; +}; + +kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]], + device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], + uint3 thread_position_in_grid [[thread_position_in_grid]], + uint3 threads_per_threadgroup [[threads_per_threadgroup]], + uint3 threadgroups_per_grid [[threads_per_grid]]) +{ + // Determine what slice of the stride copies this invocation will perform. + int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); + + int copiesRequired = constant_buffers.stride_arguments->pixelCount; + + // Find the copies that this invocation should perform. + + // - Copies that all invocations perform. + int allInvocationCopies = copiesRequired / invocations; + + // - Extra remainder copy that this invocation performs. + int index = int(thread_position_in_grid.x); + int extra = (index < (copiesRequired % invocations)) ? 1 : 0; + + int copyCount = allInvocationCopies + extra; + + // Finally, get the starting offset. Make sure to count extra copies. + + int startCopy = allInvocationCopies * index + min(copiesRequired % invocations, index); + + int srcOffset = startCopy * 2; + int dstOffset = constant_buffers.stride_arguments->dstStartOffset + startCopy; + + // Perform the conversion for this region. + for (int i = 0; i < copyCount; i++) + { + float depth = as_type(storage_buffers.in_data->data[srcOffset++]); + uint stencil = storage_buffers.in_data->data[srcOffset++]; + + uint rescaledDepth = uint(clamp(depth, 0.0, 1.0) * 16777215.0); + + storage_buffers.out_data->data[dstOffset++] = (rescaledDepth << 8) | (stencil & 0xff); + } +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 82e38def1..bf13aea7f 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -277,9 +277,18 @@ namespace Ryujinx.Graphics.Metal var autoBuffer = Renderer.BufferManager.GetBuffer(range.Handle, true); var mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; - // TODO: D32S8 conversion via temp copy holder + if (PrepareOutputBuffer(cbs, hostSize, mtlBuffer, out MTLBuffer copyToBuffer, out BufferHolder tempCopyHolder)) + { + offset = 0; + } - CopyFromOrToBuffer(cbs, mtlBuffer, MtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset: offset, stride: stride); + CopyFromOrToBuffer(cbs, copyToBuffer, MtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride); + + if (tempCopyHolder != null) + { + CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset); + tempCopyHolder.Dispose(); + } } public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) @@ -287,27 +296,62 @@ namespace Ryujinx.Graphics.Metal return new Texture(Device, Renderer, Pipeline, info, _identitySwizzleHandle, firstLayer, firstLevel); } - private int GetBufferDataLength(int size) - { - // TODO: D32S8 conversion - - return size; - } - private void CopyDataToBuffer(Span storage, ReadOnlySpan input) { - // TODO: D32S8 conversion + if (NeedsD24S8Conversion()) + { + FormatConverter.ConvertD24S8ToD32FS8(storage, input); + return; + } input.CopyTo(storage); } private ReadOnlySpan GetDataFromBuffer(ReadOnlySpan storage, int size, Span output) { - // TODO: D32S8 conversion + if (NeedsD24S8Conversion()) + { + if (output.IsEmpty) + { + output = new byte[GetBufferDataLength(size)]; + } + + FormatConverter.ConvertD32FS8ToD24S8(output, storage); + return output; + } return storage; } + private bool PrepareOutputBuffer(CommandBufferScoped cbs, int hostSize, MTLBuffer target, out MTLBuffer copyTarget, out BufferHolder copyTargetHolder) + { + if (NeedsD24S8Conversion()) + { + copyTargetHolder = Renderer.BufferManager.Create(hostSize); + copyTarget = copyTargetHolder.GetBuffer().Get(cbs, 0, hostSize).Value; + + return true; + } + + copyTarget = target; + copyTargetHolder = null; + + return false; + } + + private void CopyDataToOutputBuffer(CommandBufferScoped cbs, BufferHolder hostData, Auto copyTarget, int hostSize, int dstOffset) + { + if (NeedsD24S8Conversion()) + { + Renderer.HelperShader.ConvertD32S8ToD24S8(cbs, hostData, copyTarget, hostSize / (2 * sizeof(int)), dstOffset); + } + } + + private bool NeedsD24S8Conversion() + { + return FormatTable.IsD24S8(Info.Format) && MtlFormat == MTLPixelFormat.Depth32FloatStencil8; + } + public void CopyFromOrToBuffer( CommandBufferScoped cbs, MTLBuffer buffer, @@ -564,6 +608,16 @@ namespace Ryujinx.Graphics.Metal buffer.Dispose(); } + private int GetBufferDataLength(int length) + { + if (NeedsD24S8Conversion()) + { + return length * 2; + } + + return length; + } + public void SetStorage(BufferRange buffer) { throw new NotImplementedException(); -- 2.47.1 From e406b67690c52aaa52ca5bf682f32a9d988ab999 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 13 Aug 2024 23:18:57 +0100 Subject: [PATCH 339/368] Remove RenderPipelineDescriptorResult --- .../State/PipelineState.cs | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs index 85e6907bd..9f88f3061 100644 --- a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs +++ b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs @@ -103,24 +103,6 @@ namespace Ryujinx.Graphics.Metal // Advanced blend not supported - private struct RenderPipelineDescriptorResult : IDisposable - { - public MTLRenderPipelineDescriptor Pipeline; - private MTLVertexDescriptor _vertex; - - public RenderPipelineDescriptorResult(MTLRenderPipelineDescriptor pipeline, MTLVertexDescriptor vertex) - { - Pipeline = pipeline; - _vertex = vertex; - } - - public void Dispose() - { - Pipeline.Dispose(); - _vertex.Dispose(); - } - } - private readonly void BuildColorAttachment(MTLRenderPipelineColorAttachmentDescriptor descriptor, ColorBlendStateUid blendState) { descriptor.PixelFormat = blendState.PixelFormat; @@ -162,7 +144,7 @@ namespace Ryujinx.Graphics.Metal return vertexDescriptor; } - private RenderPipelineDescriptorResult CreateRenderDescriptor(Program program) + private MTLRenderPipelineDescriptor CreateRenderDescriptor(Program program) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); @@ -223,7 +205,7 @@ namespace Ryujinx.Graphics.Metal renderPipelineDescriptor.FragmentFunction = program.FragmentFunction; } - return new RenderPipelineDescriptorResult(renderPipelineDescriptor, vertexDescriptor); + return renderPipelineDescriptor; } public MTLRenderPipelineState CreateRenderPipeline(MTLDevice device, Program program) @@ -233,10 +215,10 @@ namespace Ryujinx.Graphics.Metal return pipelineState; } - using RenderPipelineDescriptorResult descriptors = CreateRenderDescriptor(program); + using var descriptor = CreateRenderDescriptor(program); var error = new NSError(IntPtr.Zero); - pipelineState = device.NewRenderPipelineState(descriptors.Pipeline, ref error); + pipelineState = device.NewRenderPipelineState(descriptor, ref error); if (error != IntPtr.Zero) { Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); -- 2.47.1 From 38b2cf9b8386f023913edf433aedc8926c5d6750 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 2 Sep 2024 13:30:48 +0200 Subject: [PATCH 340/368] Fix null resources breaking arg buffer alignment --- .../EncoderStateManager.cs | 315 ++++++++++-------- 1 file changed, 183 insertions(+), 132 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 9761de50b..6e5fa01d6 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -1147,29 +1147,33 @@ namespace Ryujinx.Graphics.Metal var autoBuffer = buffer.Buffer; var offset = 0; - if (autoBuffer == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - MTLBuffer mtlBuffer; - - if (range.HasValue) + if (autoBuffer != null) { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + MTLBuffer mtlBuffer; - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; + nativePtr = mtlBuffer.NativePtr; } MTLRenderStages renderStages = 0; if ((segment.Stages & ResourceStages.Vertex) != 0) { - vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + vertResourceIds[vertResourceIdIndex] = gpuAddress; vertResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageVertex; @@ -1177,13 +1181,13 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Fragment) != 0) { - fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + fragResourceIds[fragResourceIdIndex] = gpuAddress; fragResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages)); + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); } break; case Constants.StorageBuffersSetIndex: @@ -1197,29 +1201,33 @@ namespace Ryujinx.Graphics.Metal var autoBuffer = buffer.Buffer; var offset = 0; - if (autoBuffer == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - MTLBuffer mtlBuffer; - - if (range.HasValue) + if (autoBuffer != null) { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + MTLBuffer mtlBuffer; - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; + nativePtr = mtlBuffer.NativePtr; } MTLRenderStages renderStages = 0; if ((segment.Stages & ResourceStages.Vertex) != 0) { - vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + vertResourceIds[vertResourceIdIndex] = gpuAddress; vertResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageVertex; @@ -1227,13 +1235,13 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Fragment) != 0) { - fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + fragResourceIds[fragResourceIdIndex] = gpuAddress; fragResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages)); + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); } break; case Constants.TexturesSetIndex: @@ -1247,23 +1255,27 @@ namespace Ryujinx.Graphics.Metal var storage = texture.Storage; - if (storage == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - if (storage is TextureBuffer textureBuffer) + if (storage != null) { - textureBuffer.RebuildStorage(false); - } + if (storage is TextureBuffer textureBuffer) + { + textureBuffer.RebuildStorage(false); + } - var mtlTexture = storage.GetHandle(); + var mtlTexture = storage.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; + } MTLRenderStages renderStages = 0; if ((segment.Stages & ResourceStages.Vertex) != 0) { - vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIds[vertResourceIdIndex] = gpuAddress; vertResourceIdIndex++; if (texture.Sampler != null) @@ -1277,7 +1289,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Fragment) != 0) { - fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIds[fragResourceIdIndex] = gpuAddress; fragResourceIdIndex++; if (texture.Sampler != null) @@ -1289,7 +1301,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages)); + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); } } else @@ -1305,19 +1317,24 @@ namespace Ryujinx.Graphics.Metal { TextureRef texture = textures[i]; - if (texture.Storage == null) + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; + + if (texture.Storage != null) { - continue; + var mtlTexture = texture.Storage.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; } - var mtlTexture = texture.Storage.GetHandle(); samplers[i] = texture.Sampler; MTLRenderStages renderStages = 0; if ((segment.Stages & ResourceStages.Vertex) != 0) { - vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIds[vertResourceIdIndex] = gpuAddress; vertResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageVertex; @@ -1325,31 +1342,33 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Fragment) != 0) { - fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIds[fragResourceIdIndex] = gpuAddress; fragResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages)); + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); } foreach (var sampler in samplers) { - if (sampler == null) + ulong gpuAddress = 0; + + if (sampler != null) { - continue; + gpuAddress = sampler.GetSampler().GpuResourceID._impl; } if ((segment.Stages & ResourceStages.Vertex) != 0) { - vertResourceIds[vertResourceIdIndex] = sampler.GetSampler().GpuResourceID._impl; + vertResourceIds[vertResourceIdIndex] = gpuAddress; vertResourceIdIndex++; } if ((segment.Stages & ResourceStages.Fragment) != 0) { - fragResourceIds[fragResourceIdIndex] = sampler.GetSampler().GpuResourceID._impl; + fragResourceIds[fragResourceIdIndex] = gpuAddress; fragResourceIdIndex++; } } @@ -1360,20 +1379,24 @@ namespace Ryujinx.Graphics.Metal foreach (TextureBuffer bufferTexture in bufferTextures) { - if (bufferTexture == null) + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; + + if (bufferTexture != null) { - continue; + bufferTexture.RebuildStorage(false); + + var mtlTexture = bufferTexture.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; } - bufferTexture.RebuildStorage(false); - - var mtlTexture = bufferTexture.GetHandle(); - MTLRenderStages renderStages = 0; if ((segment.Stages & ResourceStages.Vertex) != 0) { - vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIds[vertResourceIdIndex] = gpuAddress; vertResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageVertex; @@ -1381,13 +1404,13 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Fragment) != 0) { - fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIds[fragResourceIdIndex] = gpuAddress; fragResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages)); + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); } } } @@ -1403,30 +1426,34 @@ namespace Ryujinx.Graphics.Metal var storage = image.Storage; - if (storage == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - var mtlTexture = storage.GetHandle(); + if (storage != null) + { + var mtlTexture = storage.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; + } MTLRenderStages renderStages = 0; if ((segment.Stages & ResourceStages.Vertex) != 0) { - vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIds[vertResourceIdIndex] = gpuAddress; vertResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageVertex; } if ((segment.Stages & ResourceStages.Fragment) != 0) { - fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIds[fragResourceIdIndex] = gpuAddress; fragResourceIdIndex++; renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); } } break; @@ -1485,28 +1512,32 @@ namespace Ryujinx.Graphics.Metal var autoBuffer = buffer.Buffer; var offset = 0; - if (autoBuffer == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - MTLBuffer mtlBuffer; - - if (range.HasValue) + if (autoBuffer != null) { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + MTLBuffer mtlBuffer; - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; + nativePtr = mtlBuffer.NativePtr; } if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, 0)); - resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); + resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } } @@ -1522,28 +1553,32 @@ namespace Ryujinx.Graphics.Metal var autoBuffer = buffer.Buffer; var offset = 0; - if (autoBuffer == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - MTLBuffer mtlBuffer; - - if (range.HasValue) + if (autoBuffer != null) { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + MTLBuffer mtlBuffer; - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; + nativePtr = mtlBuffer.NativePtr; } if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); - resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); + resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } } @@ -1559,22 +1594,26 @@ namespace Ryujinx.Graphics.Metal var storage = texture.Storage; - if (storage == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - if (storage is TextureBuffer textureBuffer) + if (storage != null) { - textureBuffer.RebuildStorage(false); - } + if (storage is TextureBuffer textureBuffer) + { + textureBuffer.RebuildStorage(false); + } - var mtlTexture = storage.GetHandle(); + var mtlTexture = storage.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; + } if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, 0)); - resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); + resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; if (texture.Sampler != null) @@ -1598,17 +1637,21 @@ namespace Ryujinx.Graphics.Metal { TextureRef texture = textures[i]; - if (texture.Storage == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - var mtlTexture = texture.Storage.GetHandle(); + if (texture.Storage != null) + { + var mtlTexture = texture.Storage.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; + } if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, 0)); - resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); + resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; samplers[i] = texture.Sampler; @@ -1630,19 +1673,23 @@ namespace Ryujinx.Graphics.Metal foreach (TextureBuffer bufferTexture in bufferTextures) { - if (bufferTexture == null) + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; + + if (bufferTexture != null) { - continue; + bufferTexture.RebuildStorage(false); + + var mtlTexture = bufferTexture.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; } - bufferTexture.RebuildStorage(false); - - var mtlTexture = bufferTexture.GetHandle(); - if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, 0)); - resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); + resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } } @@ -1662,17 +1709,21 @@ namespace Ryujinx.Graphics.Metal var storage = image.Storage; - if (storage == null) - { - continue; - } + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; - var mtlTexture = storage.GetHandle(); + if (storage != null) + { + var mtlTexture = storage.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; + } if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); - resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); + resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } } -- 2.47.1 From 51e85ed38bc62e9d86084944e7abb93ba60ee8fc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:55:30 +0100 Subject: [PATCH 341/368] Metal: Unsupported topology indexed draw conversion (#40) * Convert unsupported indexed buffer topologies * Fix index count and dispatch size * Cleanup * Fix typos --- src/Ryujinx.Graphics.Metal/BufferHolder.cs | 29 +++++ src/Ryujinx.Graphics.Metal/BufferManager.cs | 10 ++ src/Ryujinx.Graphics.Metal/CacheByRange.cs | 108 ++++++------------ src/Ryujinx.Graphics.Metal/HelperShader.cs | 66 ++++++++++- .../IndexBufferPattern.cs | 23 ---- .../IndexBufferState.cs | 37 ++++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 29 +++-- .../Ryujinx.Graphics.Metal.csproj | 1 + .../Shaders/ConvertIndexBuffer.metal | 59 ++++++++++ 9 files changed, 256 insertions(+), 106 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs index 47e9cd0e3..cc86a403f 100644 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Metal/BufferHolder.cs @@ -318,6 +318,35 @@ namespace Ryujinx.Graphics.Metal return holder.GetBuffer(); } + public Auto GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize) + { + if (!BoundToRange(offset, ref size)) + { + return null; + } + + var key = new TopologyConversionCacheKey(_renderer, pattern, indexSize); + + if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) + { + // The destination index size is always I32. + + int indexCount = size / indexSize; + + int convertedCount = pattern.GetConvertedCount(indexCount); + + holder = _renderer.BufferManager.Create(convertedCount * 4); + + _renderer.HelperShader.ConvertIndexBuffer(cbs, this, holder, pattern, indexSize, offset, indexCount); + + key.SetBuffer(holder.GetBuffer()); + + _cachedConvertedBuffers.Add(offset, size, key, holder); + } + + return holder.GetBuffer(); + } + public bool TryGetCachedConvertedBuffer(int offset, int size, ICacheKey key, out BufferHolder holder) { return _cachedConvertedBuffers.TryGetValue(offset, size, key, out holder); diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs index 71620f424..07a686223 100644 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ b/src/Ryujinx.Graphics.Metal/BufferManager.cs @@ -177,6 +177,16 @@ namespace Ryujinx.Graphics.Metal return null; } + public Auto GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize) + { + if (TryGetBuffer(handle, out var holder)) + { + return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize); + } + + return null; + } + public PinnedSpan GetData(BufferHandle handle, int offset, int size) { if (TryGetBuffer(handle, out var holder)) diff --git a/src/Ryujinx.Graphics.Metal/CacheByRange.cs b/src/Ryujinx.Graphics.Metal/CacheByRange.cs index 80a0c1018..e2eb24f66 100644 --- a/src/Ryujinx.Graphics.Metal/CacheByRange.cs +++ b/src/Ryujinx.Graphics.Metal/CacheByRange.cs @@ -39,80 +39,42 @@ namespace Ryujinx.Graphics.Metal } } - // [SupportedOSPlatform("macos")] - // struct AlignedVertexBufferCacheKey : ICacheKey - // { - // private readonly int _stride; - // private readonly int _alignment; - // - // // Used to notify the pipeline that bindings have invalidated on dispose. - // // private readonly MetalRenderer _renderer; - // // private Auto _buffer; - // - // public AlignedVertexBufferCacheKey(MetalRenderer renderer, int stride, int alignment) - // { - // // _renderer = renderer; - // _stride = stride; - // _alignment = alignment; - // // _buffer = null; - // } - // - // public readonly bool KeyEqual(ICacheKey other) - // { - // return other is AlignedVertexBufferCacheKey entry && - // entry._stride == _stride && - // entry._alignment == _alignment; - // } - // - // public void SetBuffer(Auto buffer) - // { - // // _buffer = buffer; - // } - // - // public readonly void Dispose() - // { - // // TODO: Tell pipeline buffer is dirty! - // // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); - // } - // } + [SupportedOSPlatform("macos")] + struct TopologyConversionCacheKey : ICacheKey + { + private readonly IndexBufferPattern _pattern; + private readonly int _indexSize; - // [SupportedOSPlatform("macos")] - // struct TopologyConversionCacheKey : ICacheKey - // { - // // TODO: Patterns - // // private readonly IndexBufferPattern _pattern; - // private readonly int _indexSize; - // - // // Used to notify the pipeline that bindings have invalidated on dispose. - // // private readonly MetalRenderer _renderer; - // // private Auto _buffer; - // - // public TopologyConversionCacheKey(MetalRenderer renderer, /*IndexBufferPattern pattern, */int indexSize) - // { - // // _renderer = renderer; - // // _pattern = pattern; - // _indexSize = indexSize; - // // _buffer = null; - // } - // - // public readonly bool KeyEqual(ICacheKey other) - // { - // return other is TopologyConversionCacheKey entry && - // // entry._pattern == _pattern && - // entry._indexSize == _indexSize; - // } - // - // public void SetBuffer(Auto buffer) - // { - // // _buffer = buffer; - // } - // - // public readonly void Dispose() - // { - // // TODO: Tell pipeline buffer is dirty! - // // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); - // } - // } + // Used to notify the pipeline that bindings have invalidated on dispose. + // private readonly MetalRenderer _renderer; + // private Auto _buffer; + + public TopologyConversionCacheKey(MetalRenderer renderer, IndexBufferPattern pattern, int indexSize) + { + // _renderer = renderer; + // _buffer = null; + _pattern = pattern; + _indexSize = indexSize; + } + + public readonly bool KeyEqual(ICacheKey other) + { + return other is TopologyConversionCacheKey entry && + entry._pattern == _pattern && + entry._indexSize == _indexSize; + } + + public void SetBuffer(Auto buffer) + { + // _buffer = buffer; + } + + public readonly void Dispose() + { + // TODO: Tell pipeline buffer is dirty! + // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); + } + } [SupportedOSPlatform("macos")] readonly struct Dependency diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 8039641ea..a4a1215a6 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -33,6 +33,7 @@ namespace Ryujinx.Graphics.Metal private readonly IProgram _programDepthStencilClear; private readonly IProgram _programStrideChange; private readonly IProgram _programConvertD32S8ToD24S8; + private readonly IProgram _programConvertIndexBuffer; private readonly IProgram _programDepthBlit; private readonly IProgram _programDepthBlitMs; private readonly IProgram _programStencilBlit; @@ -163,6 +164,17 @@ namespace Ryujinx.Graphics.Metal new ShaderSource(convertD32S8ToD24S8Source, ShaderStage.Compute, TargetLanguage.Msl) ], convertD32S8ToD24S8ResourceLayout, device, new ComputeSize(64, 1, 1)); + var convertIndexBufferLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build(); + + var convertIndexBufferSource = ReadMsl("ConvertIndexBuffer.metal"); + _programConvertIndexBuffer = new Program( + [ + new ShaderSource(convertIndexBufferSource, ShaderStage.Compute, TargetLanguage.Msl) + ], convertIndexBufferLayout, device, new ComputeSize(16, 1, 1)); + var depthBlitSource = ReadMsl("DepthBlit.metal"); _programDepthBlit = new Program( [ @@ -574,7 +586,7 @@ namespace Ryujinx.Graphics.Metal var srcBuffer = src.GetBuffer(); var dstBuffer = dst.GetBuffer(); - const int ParamsBufferSize = 16; + const int ParamsBufferSize = 4 * sizeof(int); // Save current state _pipeline.SwapState(_helperShaderState); @@ -636,6 +648,58 @@ namespace Ryujinx.Graphics.Metal _pipeline.SwapState(null); } + public void ConvertIndexBuffer( + CommandBufferScoped cbs, + BufferHolder src, + BufferHolder dst, + IndexBufferPattern pattern, + int indexSize, + int srcOffset, + int indexCount) + { + // TODO: Support conversion with primitive restart enabled. + + int primitiveCount = pattern.GetPrimitiveCount(indexCount); + int outputIndexSize = 4; + + var srcBuffer = src.GetBuffer(); + var dstBuffer = dst.GetBuffer(); + + const int ParamsBufferSize = 16 * sizeof(int); + + // Save current state + _pipeline.SwapState(_helperShaderState); + + Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; + + shaderParams[8] = pattern.PrimitiveVertices; + shaderParams[9] = pattern.PrimitiveVerticesOut; + shaderParams[10] = indexSize; + shaderParams[11] = outputIndexSize; + shaderParams[12] = pattern.BaseIndex; + shaderParams[13] = pattern.IndexStride; + shaderParams[14] = srcOffset; + shaderParams[15] = primitiveCount; + + pattern.OffsetIndex.CopyTo(shaderParams[..pattern.OffsetIndex.Length]); + + using var patternScoped = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); + patternScoped.Holder.SetDataUnchecked(patternScoped.Offset, shaderParams); + + Span> sbRanges = new Auto[2]; + + sbRanges[0] = srcBuffer; + sbRanges[1] = dstBuffer; + _pipeline.SetStorageBuffers(1, sbRanges); + _pipeline.SetStorageBuffers([new BufferAssignment(3, patternScoped.Range)]); + + _pipeline.SetProgram(_programConvertIndexBuffer); + _pipeline.DispatchCompute(BitUtils.DivRoundUp(primitiveCount, 16), 1, 1, "Convert Index Buffer"); + + // Restore previous state + _pipeline.SwapState(null); + } + public unsafe void ClearColor( int index, ReadOnlySpan clearColor, diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs b/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs index 7292b3134..24e3222fe 100644 --- a/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs +++ b/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs @@ -1,6 +1,5 @@ using Ryujinx.Graphics.GAL; using System; -using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -49,28 +48,6 @@ namespace Ryujinx.Graphics.Metal return primitiveCount * OffsetIndex.Length; } - public IEnumerable GetIndexMapping(int indexCount) - { - int primitiveCount = GetPrimitiveCount(indexCount); - int index = BaseIndex; - - for (int i = 0; i < primitiveCount; i++) - { - if (RepeatStart) - { - // Used for triangle fan - yield return 0; - } - - for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++) - { - yield return index + OffsetIndex[j]; - } - - index += IndexStride; - } - } - public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount) { int primitiveCount = GetPrimitiveCount(vertexCount); diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs b/src/Ryujinx.Graphics.Metal/IndexBufferState.cs index 7cd2ff42e..411df9685 100644 --- a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs +++ b/src/Ryujinx.Graphics.Metal/IndexBufferState.cs @@ -62,5 +62,42 @@ namespace Ryujinx.Graphics.Metal return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt16); } + + public (MTLBuffer, int, MTLIndexType) GetConvertedIndexBuffer( + MetalRenderer renderer, + CommandBufferScoped cbs, + int firstIndex, + int indexCount, + int convertedCount, + IndexBufferPattern pattern) + { + // Convert the index buffer using the given pattern. + int indexSize = GetIndexSize(); + + int firstIndexOffset = firstIndex * indexSize; + + var autoBuffer = renderer.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize); + + int size = convertedCount * 4; + + if (autoBuffer != null) + { + DisposableBuffer buffer = autoBuffer.Get(cbs, 0, size); + + return (buffer.Value, 0, MTLIndexType.UInt32); + } + + return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt32); + } + + private int GetIndexSize() + { + return _type switch + { + IndexType.UInt => 4, + IndexType.UShort => 2, + _ => 1, + }; + } } } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 6b42578ea..f6a5e0908 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -404,6 +404,8 @@ namespace Ryujinx.Graphics.Metal return; } + var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); + if (TopologyUnsupported(_encoderStateManager.Topology)) { var pattern = GetIndexBufferPattern(); @@ -412,7 +414,6 @@ namespace Ryujinx.Graphics.Metal var buffer = _renderer.BufferManager.GetBuffer(handle, false); var mtlBuffer = buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value; - var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); var renderCommandEncoder = GetOrCreateRenderEncoder(true); renderCommandEncoder.DrawIndexedPrimitives( @@ -424,7 +425,6 @@ namespace Ryujinx.Graphics.Metal } else { - var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); var renderCommandEncoder = GetOrCreateRenderEncoder(true); if (debugGroupName != String.Empty) @@ -483,15 +483,26 @@ namespace Ryujinx.Graphics.Metal return; } - // TODO: Reindex unsupported topologies - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - Logger.Warning?.Print(LogClass.Gpu, $"Drawing indexed with unsupported topology: {_encoderStateManager.Topology}"); - } + MTLBuffer mtlBuffer; + int offset; + MTLIndexType type; + int finalIndexCount = indexCount; var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - (MTLBuffer mtlBuffer, int offset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); + if (TopologyUnsupported(_encoderStateManager.Topology)) + { + var pattern = GetIndexBufferPattern(); + int convertedCount = pattern.GetConvertedCount(indexCount); + + finalIndexCount = convertedCount; + + (mtlBuffer, offset, type) = _encoderStateManager.IndexBuffer.GetConvertedIndexBuffer(_renderer, Cbs, firstIndex, indexCount, convertedCount, pattern); + } + else + { + (mtlBuffer, offset, type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); + } if (mtlBuffer.NativePtr != IntPtr.Zero) { @@ -499,7 +510,7 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.DrawIndexedPrimitives( primitiveType, - (ulong)indexCount, + (ulong)finalIndexCount, type, mtlBuffer, (ulong)offset, diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 0839c426a..cc1345598 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal b/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal new file mode 100644 index 000000000..c8fee5818 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal @@ -0,0 +1,59 @@ +#include + +using namespace metal; + +struct IndexBufferPattern { + int pattern[8]; + int primitiveVertices; + int primitiveVerticesOut; + int indexSize; + int indexSizeOut; + int baseIndex; + int indexStride; + int srcOffset; + int totalPrimitives; +}; + +struct InData { + uint8_t data[1]; +}; + +struct OutData { + uint8_t data[1]; +}; + +struct StorageBuffers { + device InData* in_data; + device OutData* out_data; + constant IndexBufferPattern* index_buffer_pattern; +}; + +kernel void kernelMain(device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], + uint3 thread_position_in_grid [[thread_position_in_grid]]) +{ + int primitiveIndex = int(thread_position_in_grid.x); + if (primitiveIndex >= storage_buffers.index_buffer_pattern->totalPrimitives) + { + return; + } + + int inOffset = primitiveIndex * storage_buffers.index_buffer_pattern->indexStride; + int outOffset = primitiveIndex * storage_buffers.index_buffer_pattern->primitiveVerticesOut; + + for (int i = 0; i < storage_buffers.index_buffer_pattern->primitiveVerticesOut; i++) + { + int j; + int io = max(0, inOffset + storage_buffers.index_buffer_pattern->baseIndex + storage_buffers.index_buffer_pattern->pattern[i]) * storage_buffers.index_buffer_pattern->indexSize; + int oo = (outOffset + i) * storage_buffers.index_buffer_pattern->indexSizeOut; + + for (j = 0; j < storage_buffers.index_buffer_pattern->indexSize; j++) + { + storage_buffers.out_data->data[oo + j] = storage_buffers.in_data->data[storage_buffers.index_buffer_pattern->srcOffset + io + j]; + } + + for(; j < storage_buffers.index_buffer_pattern->indexSizeOut; j++) + { + storage_buffers.out_data->data[oo + j] = uint8_t(0); + } + } +} -- 2.47.1 From dce6b9484180c5427367c8b4e32a31eb704bcdbf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 3 Sep 2024 20:53:31 +0200 Subject: [PATCH 342/368] Style --- src/Ryujinx.Graphics.Metal/CacheByRange.cs | 2 +- src/Ryujinx.Headless.SDL2/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/CacheByRange.cs b/src/Ryujinx.Graphics.Metal/CacheByRange.cs index e2eb24f66..76515808f 100644 --- a/src/Ryujinx.Graphics.Metal/CacheByRange.cs +++ b/src/Ryujinx.Graphics.Metal/CacheByRange.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Metal } [SupportedOSPlatform("macos")] - struct TopologyConversionCacheKey : ICacheKey + readonly struct TopologyConversionCacheKey : ICacheKey { private readonly IndexBufferPattern _pattern; private readonly int _indexSize; diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index fad45faee..ea789095e 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -18,10 +18,10 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Shader; +using Ryujinx.Graphics.Metal; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan.MoltenVK; -using Ryujinx.Graphics.Metal; using Ryujinx.Headless.SDL2.Metal; using Ryujinx.Headless.SDL2.OpenGL; using Ryujinx.Headless.SDL2.Vulkan; -- 2.47.1 From b1e52628936f5c6345e7a5f24bbb5c39dfe1089f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Sep 2024 13:18:48 +0200 Subject: [PATCH 343/368] Fix invalid depth stencil state when no depth stencil is present Partially fixes Sonic Frontiers and Castlevania Dominus Collection --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 6e5fa01d6..b61856777 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Metal private readonly BufferManager _bufferManager; private readonly DepthStencilCache _depthStencilCache; + private readonly MTLDepthStencilState _defaultState; private readonly EncoderState _mainState = new(); private EncoderState _currentState; @@ -44,6 +45,8 @@ namespace Ryujinx.Graphics.Metal _depthStencilCache = new(device); _currentState = _mainState; + _defaultState = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); + // Zero buffer byte[] zeros = new byte[ZeroBufferSize]; fixed (byte* ptr = zeros) @@ -952,9 +955,16 @@ namespace Ryujinx.Graphics.Metal private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { - MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); + if (DepthStencil != null) + { + MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); - renderCommandEncoder.SetDepthStencilState(state); + renderCommandEncoder.SetDepthStencilState(state); + } + else + { + renderCommandEncoder.SetDepthStencilState(_defaultState); + } } private readonly void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder) -- 2.47.1 From a05a9a33f1d513c93b52bfd17cc1daec94d61780 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Sep 2024 14:08:38 +0200 Subject: [PATCH 344/368] Fix typo in stride change shader Fixes Castlevania Dominus Collection --- src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal | 2 +- src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal index 4424ac531..1a7d2c574 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal @@ -27,7 +27,7 @@ kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONST device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], uint3 thread_position_in_grid [[thread_position_in_grid]], uint3 threads_per_threadgroup [[threads_per_threadgroup]], - uint3 threadgroups_per_grid [[threads_per_grid]]) + uint3 threadgroups_per_grid [[threadgroups_per_grid]]) { // Determine what slice of the stride copies this invocation will perform. diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal b/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal index 789527cbb..870ac3d78 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal @@ -28,7 +28,7 @@ kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONST device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], uint3 thread_position_in_grid [[thread_position_in_grid]], uint3 threads_per_threadgroup [[threads_per_threadgroup]], - uint3 threadgroups_per_grid [[threads_per_grid]]) + uint3 threadgroups_per_grid [[threadgroups_per_grid]]) { // Determine what slice of the stride copies this invocation will perform. int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); -- 2.47.1 From 4a81d9edc486faa299584c7d22475a98b24f2edc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Sep 2024 20:18:38 +0200 Subject: [PATCH 345/368] Add missing set texture for depth stencil blit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly fixes Sonic Frontiers & Link’s Awakening --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index a4a1215a6..c86974e47 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -474,6 +474,9 @@ namespace Ryujinx.Graphics.Metal private void BlitDepthStencilDraw(Texture src, bool isDepth) { + // TODO: Check this https://github.com/Ryujinx/Ryujinx/pull/5003/ + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); + string debugGroupName; if (isDepth) -- 2.47.1 From c0d20f86895042f7c0ec839975e52ee0f3f3fc5f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Sep 2024 20:42:53 +0200 Subject: [PATCH 346/368] Properly create stencil views of combined formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes Link’s Awakening --- src/Ryujinx.Graphics.Metal/Texture.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index bf13aea7f..1e2d83680 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -59,6 +59,17 @@ namespace Ryujinx.Graphics.Metal public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info) { var pixelFormat = FormatTable.GetFormat(Info.Format); + + if (info.DepthStencilMode == DepthStencilMode.Stencil) + { + pixelFormat = pixelFormat switch + { + MTLPixelFormat.Depth32FloatStencil8 => MTLPixelFormat.X32Stencil8, + MTLPixelFormat.Depth24UnormStencil8 => MTLPixelFormat.X24Stencil8, + _ => pixelFormat + }; + } + var textureType = Info.Target.Convert(); NSRange levels; levels.location = (ulong)firstLevel; -- 2.47.1 From b5e6f26296c22035eddc92a1acfb50b65c5e6dad Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 6 Sep 2024 13:33:35 +0200 Subject: [PATCH 347/368] Fix cubemap array length Fixes crash in Sonic Frontiers --- src/Ryujinx.Graphics.Metal/Texture.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 1e2d83680..551c98717 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -35,7 +35,14 @@ namespace Ryujinx.Graphics.Metal } else if (info.Target != Target.Cubemap) { - descriptor.ArrayLength = (ulong)Info.Depth; + if (info.Target == Target.CubemapArray) + { + descriptor.ArrayLength = (ulong)(Info.Depth / 6); + } + else + { + descriptor.ArrayLength = (ulong)Info.Depth; + } } MTLTextureSwizzleChannels swizzle = GetSwizzle(info, descriptor.PixelFormat); -- 2.47.1 From b941ef6bde2c598b0f9c9c8b43e5c9389bc380e8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 6 Sep 2024 20:00:12 +0200 Subject: [PATCH 348/368] Fix primitive id in shader gen Fixes Dark Souls --- src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs | 1 + src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 1832ea146..912b162d2 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -392,6 +392,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine("float4 position [[position, invariant]];"); context.AppendLine("bool front_facing [[front_facing]];"); context.AppendLine("float2 point_coord [[point_coord]];"); + context.AppendLine("uint primitive_id [[primitive_id]];"); } if (context.Definitions.IaIndexing) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 57c180fb4..e02d0a61f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.PointCoord => ("in.point_coord", AggregateType.Vector2 | AggregateType.FP32), IoVariable.PointSize => ("out.point_size", AggregateType.FP32), IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), - IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), + IoVariable.PrimitiveId => ("in.primitive_id", AggregateType.U32), IoVariable.SubgroupEqMask => ("thread_index_in_simdgroup >= 32 ? uint4(0, (1 << (thread_index_in_simdgroup - 32)), uint2(0)) : uint4(1 << thread_index_in_simdgroup, uint3(0))", AggregateType.Vector4 | AggregateType.U32), IoVariable.SubgroupGeMask => ("uint4(insert_bits(0u, 0xFFFFFFFF, thread_index_in_simdgroup, 32 - thread_index_in_simdgroup), uint3(0)) & (uint4((uint)((simd_vote::vote_t)simd_ballot(true) & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)simd_ballot(true) >> 32) & 0xFFFFFFFF), 0, 0))", AggregateType.Vector4 | AggregateType.U32), IoVariable.SubgroupGtMask => ("uint4(insert_bits(0u, 0xFFFFFFFF, thread_index_in_simdgroup + 1, 32 - thread_index_in_simdgroup - 1), uint3(0)) & (uint4((uint)((simd_vote::vote_t)simd_ballot(true) & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)simd_ballot(true) >> 32) & 0xFFFFFFFF), 0, 0))", AggregateType.Vector4 | AggregateType.U32), -- 2.47.1 From a710bcd874b3fde548eaab77f46133b9f592c7e2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 6 Sep 2024 23:42:59 +0200 Subject: [PATCH 349/368] Refactor binding logic + Bind image arrays --- .../EncoderStateManager.cs | 416 +++++++++--------- src/Ryujinx.Graphics.Metal/ImageArray.cs | 10 + 2 files changed, 212 insertions(+), 214 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index b61856777..50d9c9789 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -1111,6 +1111,95 @@ namespace Ryujinx.Graphics.Metal resources.VertexBuffers.Add(new BufferResource(zeroMtlBuffer, 0, Constants.ZeroBufferIndex)); } + private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForBuffer(ref BufferRef buffer) + { + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + + if (autoBuffer != null) + { + var offset = 0; + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; + nativePtr = mtlBuffer.NativePtr; + } + + return (gpuAddress, nativePtr); + } + + private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForTexture(ref TextureRef texture) + { + var storage = texture.Storage; + + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; + + if (storage != null) + { + if (storage is TextureBuffer textureBuffer) + { + textureBuffer.RebuildStorage(false); + } + + var mtlTexture = storage.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; + } + + return (gpuAddress, nativePtr); + } + + private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForImage(ref ImageRef image) + { + var storage = image.Storage; + + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; + + if (storage != null) + { + var mtlTexture = storage.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; + } + + return (gpuAddress, nativePtr); + } + + private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForTextureBuffer(ref TextureBuffer bufferTexture) + { + ulong gpuAddress = 0; + IntPtr nativePtr = IntPtr.Zero; + + if (bufferTexture != null) + { + bufferTexture.RebuildStorage(false); + + var mtlTexture = bufferTexture.GetHandle(); + + gpuAddress = mtlTexture.GpuResourceID._impl; + nativePtr = mtlTexture.NativePtr; + } + + return (gpuAddress, nativePtr); + } + private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly RenderEncoderResources resources) { var bindingSegments = program.BindingSegments[setIndex]; @@ -1152,32 +1241,7 @@ namespace Ryujinx.Graphics.Metal int index = binding + i; ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; - - var range = buffer.Range; - var autoBuffer = buffer.Buffer; - var offset = 0; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (autoBuffer != null) - { - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; - nativePtr = mtlBuffer.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForBuffer(ref buffer); MTLRenderStages renderStages = 0; @@ -1206,32 +1270,7 @@ namespace Ryujinx.Graphics.Metal int index = binding + i; ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; - - var range = buffer.Range; - var autoBuffer = buffer.Buffer; - var offset = 0; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (autoBuffer != null) - { - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; - nativePtr = mtlBuffer.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForBuffer(ref buffer); MTLRenderStages renderStages = 0; @@ -1262,24 +1301,7 @@ namespace Ryujinx.Graphics.Metal int index = binding + i; ref var texture = ref _currentState.TextureRefs[index]; - - var storage = texture.Storage; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (storage != null) - { - if (storage is TextureBuffer textureBuffer) - { - textureBuffer.RebuildStorage(false); - } - - var mtlTexture = storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForTexture(ref texture); MTLRenderStages renderStages = 0; @@ -1326,17 +1348,7 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < textures.Length; i++) { TextureRef texture = textures[i]; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (texture.Storage != null) - { - var mtlTexture = texture.Storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForTexture(ref texture); samplers[i] = texture.Sampler; @@ -1387,20 +1399,10 @@ namespace Ryujinx.Graphics.Metal { var bufferTextures = textureArray.GetBufferTextureRefs(); - foreach (TextureBuffer bufferTexture in bufferTextures) + for (int i = 0; i < bufferTextures.Length; i++) { - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (bufferTexture != null) - { - bufferTexture.RebuildStorage(false); - - var mtlTexture = bufferTexture.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } + TextureBuffer bufferTexture = bufferTextures[i]; + var (gpuAddress, nativePtr) = AddressForTextureBuffer(ref bufferTexture); MTLRenderStages renderStages = 0; @@ -1433,19 +1435,7 @@ namespace Ryujinx.Graphics.Metal int index = binding + i; ref var image = ref _currentState.ImageRefs[index]; - - var storage = image.Storage; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (storage != null) - { - var mtlTexture = storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForImage(ref image); MTLRenderStages renderStages = 0; @@ -1466,6 +1456,67 @@ namespace Ryujinx.Graphics.Metal resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); } } + else + { + var imageArray = _currentState.ImageArrayRefs[binding].Array; + + if (segment.Type != ResourceType.BufferImage) + { + var images = imageArray.GetTextureRefs(); + + for (int i = 0; i < images.Length; i++) + { + TextureRef image = images[i]; + var (gpuAddress, nativePtr) = AddressForTexture(ref image); + + MTLRenderStages renderStages = 0; + + if ((segment.Stages & ResourceStages.Vertex) != 0) + { + vertResourceIds[vertResourceIdIndex] = gpuAddress; + vertResourceIdIndex++; + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if ((segment.Stages & ResourceStages.Fragment) != 0) + { + fragResourceIds[fragResourceIdIndex] = gpuAddress; + fragResourceIdIndex++; + renderStages |= MTLRenderStages.RenderStageFragment; + } + + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); + } + } + else + { + var bufferImages = imageArray.GetBufferTextureRefs(); + + for (int i = 0; i < bufferImages.Length; i++) + { + TextureBuffer image = bufferImages[i]; + var (gpuAddress, nativePtr) = AddressForTextureBuffer(ref image); + + MTLRenderStages renderStages = 0; + + if ((segment.Stages & ResourceStages.Vertex) != 0) + { + vertResourceIds[vertResourceIdIndex] = gpuAddress; + vertResourceIdIndex++; + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if ((segment.Stages & ResourceStages.Fragment) != 0) + { + fragResourceIds[fragResourceIdIndex] = gpuAddress; + fragResourceIdIndex++; + renderStages |= MTLRenderStages.RenderStageFragment; + } + + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); + } + } + } break; } } @@ -1517,32 +1568,7 @@ namespace Ryujinx.Graphics.Metal int index = binding + i; ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; - - var range = buffer.Range; - var autoBuffer = buffer.Buffer; - var offset = 0; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (autoBuffer != null) - { - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; - nativePtr = mtlBuffer.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForBuffer(ref buffer); if ((segment.Stages & ResourceStages.Compute) != 0) { @@ -1558,32 +1584,7 @@ namespace Ryujinx.Graphics.Metal int index = binding + i; ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; - - var range = buffer.Range; - var autoBuffer = buffer.Buffer; - var offset = 0; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (autoBuffer != null) - { - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; - nativePtr = mtlBuffer.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForBuffer(ref buffer); if ((segment.Stages & ResourceStages.Compute) != 0) { @@ -1601,24 +1602,7 @@ namespace Ryujinx.Graphics.Metal int index = binding + i; ref var texture = ref _currentState.TextureRefs[index]; - - var storage = texture.Storage; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (storage != null) - { - if (storage is TextureBuffer textureBuffer) - { - textureBuffer.RebuildStorage(false); - } - - var mtlTexture = storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForTexture(ref texture); if ((segment.Stages & ResourceStages.Compute) != 0) { @@ -1646,17 +1630,7 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < textures.Length; i++) { TextureRef texture = textures[i]; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (texture.Storage != null) - { - var mtlTexture = texture.Storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } + var (gpuAddress, nativePtr) = AddressForTexture(ref texture); if ((segment.Stages & ResourceStages.Compute) != 0) { @@ -1681,20 +1655,10 @@ namespace Ryujinx.Graphics.Metal { var bufferTextures = textureArray.GetBufferTextureRefs(); - foreach (TextureBuffer bufferTexture in bufferTextures) + for (int i = 0; i < bufferTextures.Length; i++) { - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (bufferTexture != null) - { - bufferTexture.RebuildStorage(false); - - var mtlTexture = bufferTexture.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } + TextureBuffer bufferTexture = bufferTextures[i]; + var (gpuAddress, nativePtr) = AddressForTextureBuffer(ref bufferTexture); if ((segment.Stages & ResourceStages.Compute) != 0) { @@ -1709,26 +1673,50 @@ namespace Ryujinx.Graphics.Metal case Constants.ImagesSetIndex: if (!segment.IsArray) { - if (segment.Type != ResourceType.BufferTexture) + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) + int index = binding + i; + + ref var image = ref _currentState.ImageRefs[index]; + var (gpuAddress, nativePtr) = AddressForImage(ref image); + + if ((segment.Stages & ResourceStages.Compute) != 0) { - int index = binding + i; + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); + resourceIds[resourceIdIndex] = gpuAddress; + resourceIdIndex++; + } + } + } + else + { + var imageArray = _currentState.ImageArrayRefs[binding].Array; - ref var image = ref _currentState.ImageRefs[index]; + if (segment.Type != ResourceType.BufferImage) + { + var images = imageArray.GetTextureRefs(); - var storage = image.Storage; + for (int i = 0; i < images.Length; i++) + { + TextureRef image = images[i]; + var (gpuAddress, nativePtr) = AddressForTexture(ref image); - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (storage != null) + if ((segment.Stages & ResourceStages.Compute) != 0) { - var mtlTexture = storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; + resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); + resourceIds[resourceIdIndex] = gpuAddress; + resourceIdIndex++; } + } + } + else + { + var bufferImages = imageArray.GetBufferTextureRefs(); + + for (int i = 0; i < bufferImages.Length; i++) + { + TextureBuffer image = bufferImages[i]; + var (gpuAddress, nativePtr) = AddressForTextureBuffer(ref image); if ((segment.Stages & ResourceStages.Compute) != 0) { diff --git a/src/Ryujinx.Graphics.Metal/ImageArray.cs b/src/Ryujinx.Graphics.Metal/ImageArray.cs index 67b8186b1..6bcfd95f4 100644 --- a/src/Ryujinx.Graphics.Metal/ImageArray.cs +++ b/src/Ryujinx.Graphics.Metal/ImageArray.cs @@ -64,6 +64,16 @@ namespace Ryujinx.Graphics.Metal SetDirty(); } + public TextureRef[] GetTextureRefs() + { + return _textureRefs; + } + + public TextureBuffer[] GetBufferTextureRefs() + { + return _bufferTextureRefs; + } + private void SetDirty() { _pipeline.DirtyImages(); -- 2.47.1 From 8c2f3ae8d29f7af5c4f841649264070680947de8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 7 Sep 2024 12:24:40 +0200 Subject: [PATCH 350/368] Auto-backed samplers --- .../DisposableSampler.cs | 22 ++++++++++++++ src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 +-- .../EncoderStateManager.cs | 18 ++++++------ src/Ryujinx.Graphics.Metal/HelperShader.cs | 4 +-- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 12 +++++++- src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 +-- .../{Sampler.cs => SamplerHolder.cs} | 29 ++++++++++--------- src/Ryujinx.Graphics.Metal/TextureArray.cs | 4 +-- 8 files changed, 66 insertions(+), 31 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/DisposableSampler.cs rename src/Ryujinx.Graphics.Metal/{Sampler.cs => SamplerHolder.cs} (77%) diff --git a/src/Ryujinx.Graphics.Metal/DisposableSampler.cs b/src/Ryujinx.Graphics.Metal/DisposableSampler.cs new file mode 100644 index 000000000..ba041be89 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/DisposableSampler.cs @@ -0,0 +1,22 @@ +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + readonly struct DisposableSampler : IDisposable + { + public MTLSamplerState Value { get; } + + public DisposableSampler(MTLSamplerState sampler) + { + Value = sampler; + } + + public void Dispose() + { + Value.Dispose(); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index d5dd5123b..8aa816efb 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -55,10 +55,10 @@ namespace Ryujinx.Graphics.Metal { public ShaderStage Stage; public TextureBase Storage; - public Sampler Sampler; + public Auto Sampler; public Format ImageFormat; - public TextureRef(ShaderStage stage, TextureBase storage, Sampler sampler) + public TextureRef(ShaderStage stage, TextureBase storage, Auto sampler) { Stage = stage; Storage = storage; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 50d9c9789..ae3f3abbf 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -865,11 +865,11 @@ namespace Ryujinx.Graphics.Metal SignalDirty(DirtyFlags.StencilRef); } - public readonly void UpdateTextureAndSampler(ShaderStage stage, int binding, TextureBase texture, Sampler sampler) + public readonly void UpdateTextureAndSampler(ShaderStage stage, int binding, TextureBase texture, SamplerHolder samplerHolder) { if (texture != null) { - _currentState.TextureRefs[binding] = new(stage, texture, sampler); + _currentState.TextureRefs[binding] = new(stage, texture, samplerHolder.GetSampler()); } else { @@ -1312,7 +1312,7 @@ namespace Ryujinx.Graphics.Metal if (texture.Sampler != null) { - vertResourceIds[vertResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + vertResourceIds[vertResourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; vertResourceIdIndex++; } @@ -1326,7 +1326,7 @@ namespace Ryujinx.Graphics.Metal if (texture.Sampler != null) { - fragResourceIds[fragResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + fragResourceIds[fragResourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; fragResourceIdIndex++; } @@ -1343,7 +1343,7 @@ namespace Ryujinx.Graphics.Metal if (segment.Type != ResourceType.BufferTexture) { var textures = textureArray.GetTextureRefs(); - var samplers = new Sampler[textures.Length]; + var samplers = new Auto[textures.Length]; for (int i = 0; i < textures.Length; i++) { @@ -1379,7 +1379,7 @@ namespace Ryujinx.Graphics.Metal if (sampler != null) { - gpuAddress = sampler.GetSampler().GpuResourceID._impl; + gpuAddress = sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; } if ((segment.Stages & ResourceStages.Vertex) != 0) @@ -1612,7 +1612,7 @@ namespace Ryujinx.Graphics.Metal if (texture.Sampler != null) { - resourceIds[resourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + resourceIds[resourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; resourceIdIndex++; } } @@ -1625,7 +1625,7 @@ namespace Ryujinx.Graphics.Metal if (segment.Type != ResourceType.BufferTexture) { var textures = textureArray.GetTextureRefs(); - var samplers = new Sampler[textures.Length]; + var samplers = new Auto[textures.Length]; for (int i = 0; i < textures.Length; i++) { @@ -1646,7 +1646,7 @@ namespace Ryujinx.Graphics.Metal { if (sampler != null) { - resourceIds[resourceIdIndex] = sampler.GetSampler().GpuResourceID._impl; + resourceIds[resourceIdIndex] = sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; resourceIdIndex++; } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index c86974e47..039e3c595 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -47,8 +47,8 @@ namespace Ryujinx.Graphics.Metal _renderer = renderer; _pipeline = pipeline; - _samplerNearest = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); - _samplerLinear = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + _samplerNearest = new SamplerHolder(renderer, _device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); + _samplerLinear = new SamplerHolder(renderer, _device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); var blitResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 0) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 6bdc043bf..6a55e3e20 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Metal; using SharpMetal.QuartzCore; using System; +using System.Collections.Generic; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -33,9 +34,12 @@ namespace Ryujinx.Graphics.Metal internal Action InterruptAction { get; private set; } internal SyncManager SyncManager { get; private set; } + internal HashSet Samplers { get; } + public MetalRenderer(Func metalLayer) { _device = MTLDevice.CreateSystemDefaultDevice(); + Samplers = new HashSet(); if (_device.ArgumentBuffersSupport != MTLArgumentBuffersTier.Tier2) { @@ -101,7 +105,7 @@ namespace Ryujinx.Graphics.Metal public ISampler CreateSampler(SamplerCreateInfo info) { - return new Sampler(_device, info); + return new SamplerHolder(this, _device, info); } public ITexture CreateTexture(TextureCreateInfo info) @@ -281,6 +285,12 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { BackgroundResources.Dispose(); + + foreach (var sampler in Samplers) + { + sampler.Dispose(); + } + _pipeline.Dispose(); _window.Dispose(); } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index f6a5e0908..00da26419 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -768,9 +768,9 @@ namespace Ryujinx.Graphics.Metal { if (texture is TextureBase tex) { - if (sampler == null || sampler is Sampler) + if (sampler == null || sampler is SamplerHolder) { - _encoderStateManager.UpdateTextureAndSampler(stage, binding, tex, (Sampler)sampler); + _encoderStateManager.UpdateTextureAndSampler(stage, binding, tex, (SamplerHolder)sampler); } } } diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/SamplerHolder.cs similarity index 77% rename from src/Ryujinx.Graphics.Metal/Sampler.cs rename to src/Ryujinx.Graphics.Metal/SamplerHolder.cs index 1189288f6..3241efa6d 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/SamplerHolder.cs @@ -6,12 +6,17 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class Sampler : ISampler + class SamplerHolder : ISampler { - private readonly MTLSamplerState _mtlSamplerState; + private readonly MetalRenderer _renderer; + private readonly Auto _sampler; - public Sampler(MTLDevice device, SamplerCreateInfo info) + public SamplerHolder(MetalRenderer renderer, MTLDevice device, SamplerCreateInfo info) { + _renderer = renderer; + + renderer.Samplers.Add(this); + (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); MTLSamplerBorderColor borderColor = GetConstrainedBorderColor(info.BorderColor, out _); @@ -33,14 +38,9 @@ namespace Ryujinx.Graphics.Metal SupportArgumentBuffers = true }; - var samplerState = device.NewSamplerState(descriptor); + var sampler = device.NewSamplerState(descriptor); - _mtlSamplerState = samplerState; - } - - public Sampler(MTLSamplerState samplerState) - { - _mtlSamplerState = samplerState; + _sampler = new Auto(new DisposableSampler(sampler)); } private static MTLSamplerBorderColor GetConstrainedBorderColor(ColorF arbitraryBorderColor, out bool cantConstrain) @@ -74,14 +74,17 @@ namespace Ryujinx.Graphics.Metal return MTLSamplerBorderColor.OpaqueBlack; } - public MTLSamplerState GetSampler() + public Auto GetSampler() { - return _mtlSamplerState; + return _sampler; } public void Dispose() { - _mtlSamplerState.Dispose(); + if (_renderer.Samplers.Remove(this)) + { + _sampler.Dispose(); + } } } } diff --git a/src/Ryujinx.Graphics.Metal/TextureArray.cs b/src/Ryujinx.Graphics.Metal/TextureArray.cs index cfca843f7..ea2c74420 100644 --- a/src/Ryujinx.Graphics.Metal/TextureArray.cs +++ b/src/Ryujinx.Graphics.Metal/TextureArray.cs @@ -33,9 +33,9 @@ namespace Ryujinx.Graphics.Metal { ISampler sampler = samplers[i]; - if (sampler is Sampler samp) + if (sampler is SamplerHolder samp) { - _textureRefs[index + i].Sampler = samp; + _textureRefs[index + i].Sampler = samp.GetSampler(); } else { -- 2.47.1 From 216261931ec10686d21298882cdf68672a9821ec Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 7 Sep 2024 12:35:37 +0200 Subject: [PATCH 351/368] Program hash set --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 85 +++++++++------------ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 9 ++- src/Ryujinx.Graphics.Metal/Program.cs | 17 ++++- 3 files changed, 58 insertions(+), 53 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 039e3c595..53f503207 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -57,48 +57,42 @@ namespace Ryujinx.Graphics.Metal var blitSource = ReadMsl("Blit.metal"); var blitSourceF = blitSource.Replace("FORMAT", "float", StringComparison.Ordinal); - _programColorBlitF = new Program( - [ + _programColorBlitF = new Program(renderer, device, [ new ShaderSource(blitSourceF, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var blitSourceI = blitSource.Replace("FORMAT", "int"); - _programColorBlitI = new Program( - [ + _programColorBlitI = new Program(renderer, device, [ new ShaderSource(blitSourceI, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSourceI, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var blitSourceU = blitSource.Replace("FORMAT", "uint"); - _programColorBlitU = new Program( - [ + _programColorBlitU = new Program(renderer, device, [ new ShaderSource(blitSourceU, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSourceU, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var blitMsSource = ReadMsl("BlitMs.metal"); var blitMsSourceF = blitMsSource.Replace("FORMAT", "float"); - _programColorBlitMsF = new Program( - [ + _programColorBlitMsF = new Program(renderer, device, [ new ShaderSource(blitMsSourceF, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitMsSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var blitMsSourceI = blitMsSource.Replace("FORMAT", "int"); - _programColorBlitMsI = new Program( - [ + _programColorBlitMsI = new Program(renderer, device, [ new ShaderSource(blitMsSourceI, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitMsSourceI, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var blitMsSourceU = blitMsSource.Replace("FORMAT", "uint"); - _programColorBlitMsU = new Program( - [ + _programColorBlitMsU = new Program(renderer, device, [ new ShaderSource(blitMsSourceU, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitMsSourceU, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var colorClearResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build(); @@ -108,39 +102,35 @@ namespace Ryujinx.Graphics.Metal for (int i = 0; i < Constants.MaxColorAttachments; i++) { var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "float"); - _programsColorClearF.Add(new Program( - [ + _programsColorClearF.Add(new Program(renderer, device, [ new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout, device)); + ], colorClearResourceLayout)); } for (int i = 0; i < Constants.MaxColorAttachments; i++) { var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "int"); - _programsColorClearI.Add(new Program( - [ + _programsColorClearI.Add(new Program(renderer, device, [ new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout, device)); + ], colorClearResourceLayout)); } for (int i = 0; i < Constants.MaxColorAttachments; i++) { var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "uint"); - _programsColorClearU.Add(new Program( - [ + _programsColorClearU.Add(new Program(renderer, device, [ new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout, device)); + ], colorClearResourceLayout)); } var depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); - _programDepthStencilClear = new Program( - [ + _programDepthStencilClear = new Program(renderer, device, [ new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout, device); + ], colorClearResourceLayout); var strideChangeResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) @@ -148,10 +138,9 @@ namespace Ryujinx.Graphics.Metal .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); var strideChangeSource = ReadMsl("ChangeBufferStride.metal"); - _programStrideChange = new Program( - [ + _programStrideChange = new Program(renderer, device, [ new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) - ], strideChangeResourceLayout, device, new ComputeSize(64, 1, 1)); + ], strideChangeResourceLayout, new ComputeSize(64, 1, 1)); var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) @@ -159,10 +148,9 @@ namespace Ryujinx.Graphics.Metal .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); var convertD32S8ToD24S8Source = ReadMsl("ConvertD32S8ToD24S8.metal"); - _programConvertD32S8ToD24S8 = new Program( - [ + _programConvertD32S8ToD24S8 = new Program(renderer, device, [ new ShaderSource(convertD32S8ToD24S8Source, ShaderStage.Compute, TargetLanguage.Msl) - ], convertD32S8ToD24S8ResourceLayout, device, new ComputeSize(64, 1, 1)); + ], convertD32S8ToD24S8ResourceLayout, new ComputeSize(64, 1, 1)); var convertIndexBufferLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) @@ -170,38 +158,33 @@ namespace Ryujinx.Graphics.Metal .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build(); var convertIndexBufferSource = ReadMsl("ConvertIndexBuffer.metal"); - _programConvertIndexBuffer = new Program( - [ + _programConvertIndexBuffer = new Program(renderer, device, [ new ShaderSource(convertIndexBufferSource, ShaderStage.Compute, TargetLanguage.Msl) - ], convertIndexBufferLayout, device, new ComputeSize(16, 1, 1)); + ], convertIndexBufferLayout, new ComputeSize(16, 1, 1)); var depthBlitSource = ReadMsl("DepthBlit.metal"); - _programDepthBlit = new Program( - [ + _programDepthBlit = new Program(renderer, device, [ new ShaderSource(depthBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var depthBlitMsSource = ReadMsl("DepthBlitMs.metal"); - _programDepthBlitMs = new Program( - [ + _programDepthBlitMs = new Program(renderer, device, [ new ShaderSource(depthBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var stencilBlitSource = ReadMsl("StencilBlit.metal"); - _programStencilBlit = new Program( - [ + _programStencilBlit = new Program(renderer, device, [ new ShaderSource(stencilBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); var stencilBlitMsSource = ReadMsl("StencilBlitMs.metal"); - _programStencilBlitMs = new Program( - [ + _programStencilBlitMs = new Program(renderer, device, [ new ShaderSource(stencilBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout, device); + ], blitResourceLayout); } private static string ReadMsl(string fileName) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 6a55e3e20..99372d8a0 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -34,11 +34,13 @@ namespace Ryujinx.Graphics.Metal internal Action InterruptAction { get; private set; } internal SyncManager SyncManager { get; private set; } + internal HashSet Programs { get; } internal HashSet Samplers { get; } public MetalRenderer(Func metalLayer) { _device = MTLDevice.CreateSystemDefaultDevice(); + Programs = new HashSet(); Samplers = new HashSet(); if (_device.ArgumentBuffersSupport != MTLArgumentBuffersTier.Tier2) @@ -100,7 +102,7 @@ namespace Ryujinx.Graphics.Metal public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { - return new Program(shaders, info.ResourceLayout, _device, info.ComputeLocalSize); + return new Program(this, _device, shaders, info.ResourceLayout, info.ComputeLocalSize); } public ISampler CreateSampler(SamplerCreateInfo info) @@ -286,6 +288,11 @@ namespace Ryujinx.Graphics.Metal { BackgroundResources.Dispose(); + foreach (var program in Programs) + { + program.Dispose(); + } + foreach (var sampler in Samplers) { sampler.Dispose(); diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index c3e5a8d7c..37bae5817 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -19,6 +19,8 @@ namespace Ryujinx.Graphics.Metal private readonly GCHandle[] _handles; private int _successCount; + private readonly MetalRenderer _renderer; + public MTLFunction VertexFunction; public MTLFunction FragmentFunction; public MTLFunction ComputeFunction; @@ -34,8 +36,16 @@ namespace Ryujinx.Graphics.Metal // Argument buffer sizes for Fragment stage public int[] FragArgumentBufferSizes { get; } - public Program(ShaderSource[] shaders, ResourceLayout resourceLayout, MTLDevice device, ComputeSize computeLocalSize = default) + public Program( + MetalRenderer renderer, + MTLDevice device, + ShaderSource[] shaders, + ResourceLayout resourceLayout, + ComputeSize computeLocalSize = default) { + _renderer = renderer; + renderer.Programs.Add(this); + ComputeLocalSize = computeLocalSize; _shaders = shaders; _handles = new GCHandle[_shaders.Length]; @@ -253,6 +263,11 @@ namespace Ryujinx.Graphics.Metal public void Dispose() { + if (!_renderer.Programs.Remove(this)) + { + return; + } + if (_graphicsPipelineCache != null) { foreach (MTLRenderPipelineState pipeline in _graphicsPipelineCache.Values) -- 2.47.1 From d23de148123959a5fd808635557ca3264be0ac20 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 7 Sep 2024 21:38:01 +0200 Subject: [PATCH 352/368] Fix counted indirect draws Fixes Monster Hunter Rise and Apollo Justice --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 32 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 00da26419..4e270def4 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -521,6 +521,11 @@ namespace Ryujinx.Graphics.Metal } public void DrawIndexedIndirect(BufferRange indirectBuffer) + { + DrawIndexedIndirectOffset(indirectBuffer); + } + + public void DrawIndexedIndirectOffset(BufferRange indirectBuffer, int offset = 0) { // TODO: Reindex unsupported topologies if (TopologyUnsupported(_encoderStateManager.Topology)) @@ -534,7 +539,7 @@ namespace Ryujinx.Graphics.Metal var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - (MTLBuffer indexBuffer, int offset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); + (MTLBuffer indexBuffer, int indexOffset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); if (indexBuffer.NativePtr != IntPtr.Zero && buffer.NativePtr != IntPtr.Zero) { @@ -544,20 +549,26 @@ namespace Ryujinx.Graphics.Metal primitiveType, type, indexBuffer, - (ulong)offset, + (ulong)indexOffset, buffer, - (ulong)indirectBuffer.Offset); + (ulong)(indirectBuffer.Offset + offset)); } } public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - // TODO: Properly support count - - DrawIndexedIndirect(indirectBuffer); + for (int i = 0; i < maxDrawCount; i++) + { + DrawIndexedIndirectOffset(indirectBuffer, stride * i); + } } public void DrawIndirect(BufferRange indirectBuffer) + { + DrawIndirectOffset(indirectBuffer); + } + + public void DrawIndirectOffset(BufferRange indirectBuffer, int offset = 0) { if (TopologyUnsupported(_encoderStateManager.Topology)) { @@ -575,14 +586,15 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.DrawPrimitives( primitiveType, buffer, - (ulong)indirectBuffer.Offset); + (ulong)(indirectBuffer.Offset + offset)); } public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - // TODO: Properly support count - - DrawIndirect(indirectBuffer); + for (int i = 0; i < maxDrawCount; i++) + { + DrawIndirectOffset(indirectBuffer, stride * i); + } } public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) -- 2.47.1 From fe4c77788f47b9db46a8d375fdaa000d0c0bd09e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 7 Sep 2024 21:46:34 +0200 Subject: [PATCH 353/368] Fix null sampler crash --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index ae3f3abbf..679cc9daf 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -869,7 +869,7 @@ namespace Ryujinx.Graphics.Metal { if (texture != null) { - _currentState.TextureRefs[binding] = new(stage, texture, samplerHolder.GetSampler()); + _currentState.TextureRefs[binding] = new(stage, texture, samplerHolder?.GetSampler()); } else { -- 2.47.1 From 89c05ac23986f2993f289c8f924b0552be578def Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 8 Sep 2024 01:29:36 +0200 Subject: [PATCH 354/368] Check for null resources before declaring them resident --- src/Ryujinx.Graphics.Metal/EncoderStateManager.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 679cc9daf..13d276af7 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -293,7 +293,10 @@ namespace Ryujinx.Graphics.Metal foreach (var resource in _currentState.RenderEncoderResources.Resources) { - renderCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage, resource.Stages); + if (resource.MtlResource.NativePtr != IntPtr.Zero) + { + renderCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage, resource.Stages); + } } foreach (var buffer in _currentState.RenderEncoderResources.VertexBuffers) -- 2.47.1 From a5f82a99a366d844bb08c806b066dee001f14dbf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 18 Sep 2024 23:52:24 +0200 Subject: [PATCH 355/368] GAL Changes --- src/Ryujinx.Graphics.Metal/ImageArray.cs | 10 ---------- src/Ryujinx.Graphics.Metal/Pipeline.cs | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/ImageArray.cs b/src/Ryujinx.Graphics.Metal/ImageArray.cs index 6bcfd95f4..9fa0df09d 100644 --- a/src/Ryujinx.Graphics.Metal/ImageArray.cs +++ b/src/Ryujinx.Graphics.Metal/ImageArray.cs @@ -27,16 +27,6 @@ namespace Ryujinx.Graphics.Metal _pipeline = pipeline; } - public void SetFormats(int index, Format[] imageFormats) - { - for (int i = 0; i < imageFormats.Length; i++) - { - _textureRefs[index + i].ImageFormat = imageFormats[i]; - } - - SetDirty(); - } - public void SetImages(int index, ITexture[] images) { for (int i = 0; i < images.Length; i++) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 4e270def4..113974061 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -661,7 +661,7 @@ namespace Ryujinx.Graphics.Metal _encoderStateManager.UpdateIndexBuffer(buffer, type); } - public void SetImage(ShaderStage stage, int binding, ITexture image, Format imageFormat) + public void SetImage(ShaderStage stage, int binding, ITexture image) { if (image is TextureBase img) { -- 2.47.1 From 567b0a5027d3163c1a9a725307fc99109729d0d5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 19 Sep 2024 19:59:40 +0200 Subject: [PATCH 356/368] Updates --- src/Ryujinx.Graphics.Metal/Texture.cs | 8 ++++---- src/Ryujinx.Graphics.Metal/TextureBuffer.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 551c98717..4566d65d8 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -1,9 +1,9 @@ using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Foundation; using SharpMetal.Metal; using System; -using System.Buffers; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -509,7 +509,7 @@ namespace Ryujinx.Graphics.Metal return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level)); } - public void SetData(IMemoryOwner data) + public void SetData(MemoryOwner data) { var blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder(); @@ -586,14 +586,14 @@ namespace Ryujinx.Graphics.Metal CopyFromOrToBuffer(cbs, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice); } - 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); data.Dispose(); } - public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) + public void SetData(MemoryOwner data, int layer, int level, Rectangle region) { var blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder(); diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs index 8d23bad98..483cef28b 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs @@ -1,7 +1,7 @@ +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Metal; using System; -using System.Buffers; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -88,18 +88,18 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void SetData(IMemoryOwner data) + public void SetData(MemoryOwner data) { Renderer.SetBufferData(_bufferHandle, _offset, data.Memory.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(); } -- 2.47.1 From 7bdae9e55264cc46e7ed9326f8c41f441f18fd22 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 29 Sep 2024 01:06:10 +0200 Subject: [PATCH 357/368] GAL Changes --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 99372d8a0..83974b408 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -209,7 +209,8 @@ namespace Ryujinx.Graphics.Metal shaderSubgroupSize: 256, storageBufferOffsetAlignment: 16, textureBufferOffsetAlignment: 16, - gatherBiasPrecision: 0 + gatherBiasPrecision: 0, + maximumGpuMemory: 0 ); } -- 2.47.1 From d3c0971acff89aa2c21d866df2bfa3cffa376a39 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 29 Sep 2024 01:17:21 +0200 Subject: [PATCH 358/368] Set correct maximum supported anisotropy --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 83974b408..bb87b6a49 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Metal maximumTexturesPerStage: Constants.MaxTexturesPerStage, maximumImagesPerStage: Constants.MaxImagesPerStage, maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength, - maximumSupportedAnisotropy: 0, + maximumSupportedAnisotropy: 16, shaderSubgroupSize: 256, storageBufferOffsetAlignment: 16, textureBufferOffsetAlignment: 16, -- 2.47.1 From 0945ea68febd56bd2d0022ad4d4590914ee209bc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 30 Sep 2024 17:52:07 +0200 Subject: [PATCH 359/368] Make resource encoding less stupid --- .../EncoderResources.cs | 8 +- src/Ryujinx.Graphics.Metal/EncoderState.cs | 4 +- .../EncoderStateManager.cs | 105 ++++++++++-------- 3 files changed, 67 insertions(+), 50 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/EncoderResources.cs b/src/Ryujinx.Graphics.Metal/EncoderResources.cs index cfda9bcbe..562500d76 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderResources.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderResources.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Metal { - public struct RenderEncoderResources + public struct RenderEncoderBindings { public List Resources = new(); public List VertexBuffers = new(); public List FragmentBuffers = new(); - public RenderEncoderResources() { } + public RenderEncoderBindings() { } public readonly void Clear() { @@ -19,12 +19,12 @@ namespace Ryujinx.Graphics.Metal } } - public struct ComputeEncoderResources + public struct ComputeEncoderBindings { public List Resources = new(); public List Buffers = new(); - public ComputeEncoderResources() { } + public ComputeEncoderBindings() { } public readonly void Clear() { diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 8aa816efb..34de168a6 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -152,8 +152,8 @@ namespace Ryujinx.Graphics.Metal // Only to be used for present public bool ClearLoadAction = false; - public RenderEncoderResources RenderEncoderResources = new(); - public ComputeEncoderResources ComputeEncoderResources = new(); + public RenderEncoderBindings RenderEncoderBindings = new(); + public ComputeEncoderBindings ComputeEncoderBindings = new(); public EncoderState() { diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 13d276af7..0093ac148 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader; using SharpMetal.Metal; using System; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment; @@ -191,56 +192,56 @@ namespace Ryujinx.Graphics.Metal public readonly void RenderResourcesPrepass() { - _currentState.RenderEncoderResources.Clear(); + _currentState.RenderEncoderBindings.Clear(); if ((_currentState.Dirty & DirtyFlags.RenderPipeline) != 0) { - SetVertexBuffers(_currentState.VertexBuffers, ref _currentState.RenderEncoderResources); + SetVertexBuffers(_currentState.VertexBuffers, ref _currentState.RenderEncoderBindings); } if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) { - UpdateAndBind(_currentState.RenderProgram, Constants.ConstantBuffersSetIndex, ref _currentState.RenderEncoderResources); + UpdateAndBind(_currentState.RenderProgram, Constants.ConstantBuffersSetIndex, ref _currentState.RenderEncoderBindings); } if ((_currentState.Dirty & DirtyFlags.Storages) != 0) { - UpdateAndBind(_currentState.RenderProgram, Constants.StorageBuffersSetIndex, ref _currentState.RenderEncoderResources); + UpdateAndBind(_currentState.RenderProgram, Constants.StorageBuffersSetIndex, ref _currentState.RenderEncoderBindings); } if ((_currentState.Dirty & DirtyFlags.Textures) != 0) { - UpdateAndBind(_currentState.RenderProgram, Constants.TexturesSetIndex, ref _currentState.RenderEncoderResources); + UpdateAndBind(_currentState.RenderProgram, Constants.TexturesSetIndex, ref _currentState.RenderEncoderBindings); } if ((_currentState.Dirty & DirtyFlags.Images) != 0) { - UpdateAndBind(_currentState.RenderProgram, Constants.ImagesSetIndex, ref _currentState.RenderEncoderResources); + UpdateAndBind(_currentState.RenderProgram, Constants.ImagesSetIndex, ref _currentState.RenderEncoderBindings); } } public readonly void ComputeResourcesPrepass() { - _currentState.ComputeEncoderResources.Clear(); + _currentState.ComputeEncoderBindings.Clear(); if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) { - UpdateAndBind(_currentState.ComputeProgram, Constants.ConstantBuffersSetIndex, ref _currentState.ComputeEncoderResources); + UpdateAndBind(_currentState.ComputeProgram, Constants.ConstantBuffersSetIndex, ref _currentState.ComputeEncoderBindings); } if ((_currentState.Dirty & DirtyFlags.Storages) != 0) { - UpdateAndBind(_currentState.ComputeProgram, Constants.StorageBuffersSetIndex, ref _currentState.ComputeEncoderResources); + UpdateAndBind(_currentState.ComputeProgram, Constants.StorageBuffersSetIndex, ref _currentState.ComputeEncoderBindings); } if ((_currentState.Dirty & DirtyFlags.Textures) != 0) { - UpdateAndBind(_currentState.ComputeProgram, Constants.TexturesSetIndex, ref _currentState.ComputeEncoderResources); + UpdateAndBind(_currentState.ComputeProgram, Constants.TexturesSetIndex, ref _currentState.ComputeEncoderBindings); } if ((_currentState.Dirty & DirtyFlags.Images) != 0) { - UpdateAndBind(_currentState.ComputeProgram, Constants.ImagesSetIndex, ref _currentState.ComputeEncoderResources); + UpdateAndBind(_currentState.ComputeProgram, Constants.ImagesSetIndex, ref _currentState.ComputeEncoderBindings); } } @@ -291,20 +292,17 @@ namespace Ryujinx.Graphics.Metal SetScissors(renderCommandEncoder); } - foreach (var resource in _currentState.RenderEncoderResources.Resources) + foreach (var resource in _currentState.RenderEncoderBindings.Resources) { - if (resource.MtlResource.NativePtr != IntPtr.Zero) - { - renderCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage, resource.Stages); - } + renderCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage, resource.Stages); } - foreach (var buffer in _currentState.RenderEncoderResources.VertexBuffers) + foreach (var buffer in _currentState.RenderEncoderBindings.VertexBuffers) { renderCommandEncoder.SetVertexBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); } - foreach (var buffer in _currentState.RenderEncoderResources.FragmentBuffers) + foreach (var buffer in _currentState.RenderEncoderBindings.FragmentBuffers) { renderCommandEncoder.SetFragmentBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); } @@ -319,12 +317,12 @@ namespace Ryujinx.Graphics.Metal SetComputePipelineState(computeCommandEncoder); } - foreach (var resource in _currentState.ComputeEncoderResources.Resources) + foreach (var resource in _currentState.ComputeEncoderBindings.Resources) { computeCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage); } - foreach (var buffer in _currentState.ComputeEncoderResources.Buffers) + foreach (var buffer in _currentState.ComputeEncoderBindings.Buffers) { computeCommandEncoder.SetBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); } @@ -1089,7 +1087,7 @@ namespace Ryujinx.Graphics.Metal pipeline.VertexBindingDescriptionsCount = Constants.ZeroBufferIndex + 1; // TODO: move this out? } - private readonly void SetVertexBuffers(VertexBufferState[] bufferStates, ref readonly RenderEncoderResources resources) + private readonly void SetVertexBuffers(VertexBufferState[] bufferStates, ref readonly RenderEncoderBindings bindings) { for (int i = 0; i < bufferStates.Length; i++) { @@ -1097,7 +1095,7 @@ namespace Ryujinx.Graphics.Metal if (mtlBuffer.NativePtr != IntPtr.Zero) { - resources.VertexBuffers.Add(new BufferResource(mtlBuffer, (ulong)offset, (ulong)i)); + bindings.VertexBuffers.Add(new BufferResource(mtlBuffer, (ulong)offset, (ulong)i)); } } @@ -1111,7 +1109,7 @@ namespace Ryujinx.Graphics.Metal } var zeroMtlBuffer = autoZeroBuffer.Get(_pipeline.Cbs).Value; - resources.VertexBuffers.Add(new BufferResource(zeroMtlBuffer, 0, Constants.ZeroBufferIndex)); + bindings.VertexBuffers.Add(new BufferResource(zeroMtlBuffer, 0, Constants.ZeroBufferIndex)); } private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForBuffer(ref BufferRef buffer) @@ -1203,7 +1201,25 @@ namespace Ryujinx.Graphics.Metal return (gpuAddress, nativePtr); } - private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly RenderEncoderResources resources) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void AddResource(IntPtr resourcePointer, MTLResourceUsage usage, MTLRenderStages stages, ref readonly RenderEncoderBindings bindings) + { + if (resourcePointer != IntPtr.Zero) + { + bindings.Resources.Add(new Resource(new MTLResource(resourcePointer), usage, stages)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void AddResource(IntPtr resourcePointer, MTLResourceUsage usage, ref readonly ComputeEncoderBindings bindings) + { + if (resourcePointer != IntPtr.Zero) + { + bindings.Resources.Add(new Resource(new MTLResource(resourcePointer), usage, 0)); + } + } + + private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly RenderEncoderBindings bindings) { var bindingSegments = program.BindingSegments[setIndex]; @@ -1264,7 +1280,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); + AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); } break; case Constants.StorageBuffersSetIndex: @@ -1293,7 +1309,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); + AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); } break; case Constants.TexturesSetIndex: @@ -1336,7 +1352,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); + AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); } } else @@ -1373,7 +1389,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); + AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); } foreach (var sampler in samplers) @@ -1425,7 +1441,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, renderStages)); + AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); } } } @@ -1456,7 +1472,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); + AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); } } else @@ -1488,7 +1504,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); + AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); } } else @@ -1516,7 +1532,7 @@ namespace Ryujinx.Graphics.Metal renderStages |= MTLRenderStages.RenderStageFragment; } - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages)); + AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); } } } @@ -1528,18 +1544,18 @@ namespace Ryujinx.Graphics.Metal { vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds)); var mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - resources.VertexBuffers.Add(new BufferResource(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); + bindings.VertexBuffers.Add(new BufferResource(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); } if (program.FragArgumentBufferSizes[setIndex] > 0) { fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds)); var mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - resources.FragmentBuffers.Add(new BufferResource(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); + bindings.FragmentBuffers.Add(new BufferResource(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); } } - private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly ComputeEncoderResources resources) + private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly ComputeEncoderBindings bindings) { var bindingSegments = program.BindingSegments[setIndex]; @@ -1575,7 +1591,8 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); + AddResource(nativePtr, MTLResourceUsage.Read, in bindings); + bindings.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } @@ -1591,7 +1608,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); + AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } @@ -1609,7 +1626,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); + AddResource(nativePtr, MTLResourceUsage.Read, in bindings); resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; @@ -1637,7 +1654,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); + AddResource(nativePtr, MTLResourceUsage.Read, in bindings); resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; @@ -1665,7 +1682,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); + AddResource(nativePtr, MTLResourceUsage.Read, in bindings); resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } @@ -1685,7 +1702,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); + AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } @@ -1706,7 +1723,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); + AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } @@ -1723,7 +1740,7 @@ namespace Ryujinx.Graphics.Metal if ((segment.Stages & ResourceStages.Compute) != 0) { - resources.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, 0)); + AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); resourceIds[resourceIdIndex] = gpuAddress; resourceIdIndex++; } @@ -1738,7 +1755,7 @@ namespace Ryujinx.Graphics.Metal { argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); var mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value; - resources.Buffers.Add(new BufferResource(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); + bindings.Buffers.Add(new BufferResource(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); } } -- 2.47.1 From d4b9e065226a6c4e75e8f9f92ded78bf0e363eba Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 20:15:36 -0600 Subject: [PATCH 360/368] .NET 9 --- src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index cc1345598..02afb150a 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -1,7 +1,6 @@ - net8.0 true -- 2.47.1 From f3dcb80a6093a8511e513eee523511d4ae466929 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 21:52:39 -0600 Subject: [PATCH 361/368] GAL changes --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 3 +++ src/Ryujinx.Graphics.Metal/Window.cs | 6 +++--- src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index bb87b6a49..935a5b3b1 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -20,6 +20,8 @@ namespace Ryujinx.Graphics.Metal private Pipeline _pipeline; private Window _window; + + public uint ProgramCount { get; set; } = 0; public event EventHandler ScreenCaptured; public bool PreferThreading => true; @@ -102,6 +104,7 @@ namespace Ryujinx.Graphics.Metal public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { + ProgramCount++; return new Program(this, _device, shaders, info.ResourceLayout, info.ComputeLocalSize); } diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 6489b591d..65a47d217 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -137,10 +137,10 @@ namespace Ryujinx.Graphics.Metal _requestedWidth = width; _requestedHeight = height; } - - public void ChangeVSyncMode(bool vsyncEnabled) + + public void ChangeVSyncMode(VSyncMode vSyncMode) { - // _vsyncEnabled = vsyncEnabled; + //_vSyncMode = vSyncMode; } public void SetAntiAliasing(AntiAliasing effect) diff --git a/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs b/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs index e10d6eb29..5140d639b 100644 --- a/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs +++ b/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs @@ -22,8 +22,9 @@ namespace Ryujinx.Headless.SDL2.Metal GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse, - HideCursorMode hideCursorMode) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode) { } + HideCursorMode hideCursorMode, + bool ignoreControllerApplet) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) { } public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_METAL; -- 2.47.1 From e82a90993cf130fbb898e472a9f1573fc0737a44 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 22:32:37 -0600 Subject: [PATCH 362/368] UI: Properly disable selecting Metal on non-Mac platforms --- .../Views/Settings/SettingsGraphicsView.axaml | 14 ++++++++------ src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs | 18 +++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index 62c083d15..5c52d5788 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -33,18 +33,20 @@ ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}" Text="{ext:Locale SettingsTabGraphicsBackend}" Width="250" /> - + - - + + diff --git a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs index d8c88bed8..b004d9fba 100644 --- a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs @@ -56,34 +56,34 @@ namespace Ryujinx.Ava.UI.Windows { switch (navItem.Tag.ToString()) { - case "UiPage": + case nameof(UiPage): UiPage.ViewModel = ViewModel; NavPanel.Content = UiPage; break; - case "InputPage": + case nameof(InputPage): NavPanel.Content = InputPage; break; - case "HotkeysPage": + case nameof(HotkeysPage): NavPanel.Content = HotkeysPage; break; - case "SystemPage": + case nameof(SystemPage): SystemPage.ViewModel = ViewModel; NavPanel.Content = SystemPage; break; - case "CpuPage": + case nameof(CpuPage): NavPanel.Content = CpuPage; break; - case "GraphicsPage": + case nameof(GraphicsPage): NavPanel.Content = GraphicsPage; break; - case "AudioPage": + case nameof(AudioPage): NavPanel.Content = AudioPage; break; - case "NetworkPage": + case nameof(NetworkPage): NetworkPage.ViewModel = ViewModel; NavPanel.Content = NetworkPage; break; - case "LoggingPage": + case nameof(LoggingPage): NavPanel.Content = LoggingPage; break; default: -- 2.47.1 From 3aaeaf3540078ab40eb6a2594a37f56b7ffcf565 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 22:35:28 -0600 Subject: [PATCH 363/368] UI: Only allow ARM macs to select Metal backend --- src/Ryujinx/UI/ViewModels/SettingsViewModel.cs | 3 ++- src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 8b7c6d967..2d20af825 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -120,7 +120,8 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool IsMetalAvailable => OperatingSystem.IsMacOS(); + public bool IsMetalAvailable => + OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS(); diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index 5c52d5788..1f3cfd46c 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -46,7 +46,7 @@ - + -- 2.47.1 From a335c8ff2ba5a6886ec76523795871c351bdf2e4 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 22:37:15 -0600 Subject: [PATCH 364/368] UI: Collapse IsHypervisorAvailable and IsMetalAvailable into IsAppleSiliconMac --- src/Ryujinx/UI/ViewModels/SettingsViewModel.cs | 5 +---- src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml | 2 +- src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 2d20af825..0ee1c9def 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -120,12 +120,9 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool IsMetalAvailable => - OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; - public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS(); - public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; + public bool IsAppleSiliconMac => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; public bool GameDirectoryChanged { diff --git a/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml index c7f03a45d..83f908a9c 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml @@ -69,7 +69,7 @@ diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index 1f3cfd46c..c3693ac5f 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -45,7 +45,7 @@ - + -- 2.47.1 From f3545f5eae047c8c12b1cceef91e359d6b576d1f Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 23:26:47 -0600 Subject: [PATCH 365/368] UI: Auto graphics backend --- .../Configuration/GraphicsBackend.cs | 1 + src/Ryujinx/Assets/locales.json | 48 +++++++++++++++++++ src/Ryujinx/UI/Renderer/RendererHost.axaml.cs | 42 +++++++++++++++- .../UI/ViewModels/MainWindowViewModel.cs | 2 +- .../UI/ViewModels/SettingsViewModel.cs | 5 +- .../Views/Settings/SettingsGraphicsView.axaml | 3 ++ 6 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs index ba68fefbe..9d399d560 100644 --- a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs +++ b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Common.Configuration [JsonConverter(typeof(TypedStringEnumConverter))] public enum GraphicsBackend { + Auto, Vulkan, OpenGl, Metal diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 2e86df0aa..cdf43f474 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -19677,6 +19677,54 @@ "zh_TW": "選擇模擬器將使用的圖形後端。\n\n只要驅動程式是最新的,Vulkan 對所有現代顯示卡來說都更好用。Vulkan 還能在所有 GPU 廠商上實現更快的著色器編譯 (減少卡頓)。\n\nOpenGL 在舊式 Nvidia GPU、Linux 上的舊式 AMD GPU 或 VRAM 較低的 GPU 上可能會取得更好的效果,不過著色器編譯的卡頓會更嚴重。\n\n如果不確定,請設定為 Vulkan。如果您的 GPU 使用最新的圖形驅動程式也不支援 Vulkan,請設定為 OpenGL。" } }, + { + "ID": "SettingsTabGraphicsBackendAuto", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Auto", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "SettingsTabGraphicsBackendAutoTooltip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Uses Vulkan.\nOn an ARM Mac, and when playing a game that runs well under it, uses the Metal backend.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsEnableTextureRecompression", "Translations": { diff --git a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs index 7009a9377..58eba063c 100644 --- a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs +++ b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs @@ -1,8 +1,10 @@ using Avalonia; using Avalonia.Controls; +using Gommon; using Ryujinx.Common.Configuration; using Ryujinx.UI.Common.Configuration; using System; +using System.Runtime.InteropServices; namespace Ryujinx.Ava.UI.Renderer { @@ -21,13 +23,51 @@ namespace Ryujinx.Ava.UI.Renderer { GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(), GraphicsBackend.Metal => new EmbeddedWindowMetal(), - GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(), + GraphicsBackend.Vulkan or GraphicsBackend.Auto => new EmbeddedWindowVulkan(), _ => throw new NotSupportedException() }; Initialize(); } + private readonly string[] _knownGreatMetalTitles = + [ + "01006A800016E000", // Smash Ultimate + "0100000000010000", // Super Mario Odyessy + "01008C0016544000", // Sea of Stars + "01005CA01580E000", // Persona 5 + "010028600EBDA000", // Mario 3D World + ]; + + public RendererHost(string titleId) + { + InitializeComponent(); + + switch (ConfigurationState.Instance.Graphics.GraphicsBackend.Value) + { + case GraphicsBackend.Auto: + EmbeddedWindow = + OperatingSystem.IsMacOS() && + RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && + _knownGreatMetalTitles.ContainsIgnoreCase(titleId) + ? new EmbeddedWindowMetal() + : new EmbeddedWindowVulkan(); + break; + case GraphicsBackend.OpenGl: + EmbeddedWindow = new EmbeddedWindowOpenGL(); + break; + case GraphicsBackend.Metal: + EmbeddedWindow = new EmbeddedWindowMetal(); + break; + case GraphicsBackend.Vulkan: + EmbeddedWindow = new EmbeddedWindowVulkan(); + break; + } + + Initialize(); + } + + private void Initialize() { EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated; diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index ae373c267..f7cd83ed6 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -1938,7 +1938,7 @@ namespace Ryujinx.Ava.UI.ViewModels PrepareLoadScreen(); - RendererHostControl = new RendererHost(); + RendererHostControl = new RendererHost(application.Id.ToString("X16")); AppHost = new AppHost( RendererHostControl, diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 0ee1c9def..85ba203f9 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -252,7 +252,8 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsCustomResolutionScaleActive => _resolutionScale == 4; public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr; - public bool IsVulkanSelected => GraphicsBackendIndex == 0; + public bool IsVulkanSelected => + GraphicsBackendIndex == 1 || (GraphicsBackendIndex == 0 && !OperatingSystem.IsMacOS()); public bool UseHypervisor { get; set; } public bool DisableP2P { get; set; } @@ -432,7 +433,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (devices.Length == 0) { IsVulkanAvailable = false; - GraphicsBackendIndex = 1; + GraphicsBackendIndex = 2; } else { diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index c3693ac5f..a2559f393 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -39,6 +39,9 @@ HorizontalContentAlignment="Left" ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}" SelectedIndex="{Binding GraphicsBackendIndex}"> + + + -- 2.47.1 From 89f3c8235cbc1f9c63b3745e8fa859fbe6a397ae Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 23 Dec 2024 23:53:58 -0600 Subject: [PATCH 366/368] misc: More places need to factor in Auto, oops --- .../Configuration/ConfigurationState.cs | 19 +++++++-------- src/Ryujinx/AppHost.cs | 23 +++++++++++++++++-- src/Ryujinx/UI/Renderer/RendererHost.axaml.cs | 4 ++-- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index 04ddd442f..90bdc3409 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -305,14 +305,15 @@ namespace Ryujinx.UI.Common.Configuration private static GraphicsBackend DefaultGraphicsBackend() { - // Any system running macOS or returning any amount of valid Vulkan devices should default to Vulkan. - // Checks for if the Vulkan version and featureset is compatible should be performed within VulkanRenderer. - if (OperatingSystem.IsMacOS() || VulkanRenderer.GetPhysicalDevices().Length > 0) - { - return GraphicsBackend.Vulkan; - } + // Any system running macOS should default to auto, so it uses Vulkan everywhere and Metal in games where it works well. + if (OperatingSystem.IsMacOS()) + return GraphicsBackend.Auto; - return GraphicsBackend.OpenGl; - } - } + // Any system returning any amount of valid Vulkan devices should default to Vulkan. + // Checks for if the Vulkan version and featureset is compatible should be performed within VulkanRenderer. + return VulkanRenderer.GetPhysicalDevices().Length > 0 + ? GraphicsBackend.Vulkan + : GraphicsBackend.OpenGl; } + } +} diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index cbf073caa..b42bd1d49 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Threading; +using Gommon; using LibHac.Common; using LibHac.Ns; using LibHac.Tools.FsSystem; @@ -142,6 +143,23 @@ namespace Ryujinx.Ava public ulong ApplicationId { get; private set; } public bool ScreenshotRequested { get; set; } + public bool ShouldInitMetal + { + get + { + return OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && + ( + ( + ( + ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Auto && + RendererHost.KnownGreatMetalTitles.ContainsIgnoreCase(ApplicationId.ToString("X16")) + ) || + ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal + ) + ); + } + } + public AppHost( RendererHost renderer, InputManager inputManager, @@ -895,15 +913,16 @@ namespace Ryujinx.Ava // Initialize Renderer. IRenderer renderer; + GraphicsBackend backend = ConfigurationState.Instance.Graphics.GraphicsBackend; - if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) + if (backend == GraphicsBackend.Vulkan || (backend == GraphicsBackend.Auto && !ShouldInitMetal)) { renderer = VulkanRenderer.Create( ConfigurationState.Instance.Graphics.PreferredGpu, (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface, VulkanHelper.GetRequiredInstanceExtensions); } - else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal && OperatingSystem.IsMacOS()) + else if (ShouldInitMetal) { renderer = new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal).CreateSurface); } diff --git a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs index 58eba063c..8b5dd3065 100644 --- a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs +++ b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Ava.UI.Renderer Initialize(); } - private readonly string[] _knownGreatMetalTitles = + public static readonly string[] KnownGreatMetalTitles = [ "01006A800016E000", // Smash Ultimate "0100000000010000", // Super Mario Odyessy @@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.Renderer EmbeddedWindow = OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && - _knownGreatMetalTitles.ContainsIgnoreCase(titleId) + KnownGreatMetalTitles.ContainsIgnoreCase(titleId) ? new EmbeddedWindowMetal() : new EmbeddedWindowVulkan(); break; -- 2.47.1 From bff884a89cca23b20538bf92c5c8a46a34bd7bbe Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Tue, 24 Dec 2024 00:18:47 -0600 Subject: [PATCH 367/368] UI: Change backend text based on used Window type rather than configured value --- src/Ryujinx/AppHost.cs | 2 +- src/Ryujinx/UI/Renderer/RendererHost.axaml.cs | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index b42bd1d49..768e70f3c 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1142,7 +1142,7 @@ namespace Ryujinx.Ava public void InitStatus() { - _viewModel.BackendText = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch + _viewModel.BackendText = RendererHost.Backend switch { GraphicsBackend.Vulkan => "Vulkan", GraphicsBackend.OpenGl => "OpenGL", diff --git a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs index 8b5dd3065..d191a11ed 100644 --- a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs +++ b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs @@ -38,7 +38,16 @@ namespace Ryujinx.Ava.UI.Renderer "01005CA01580E000", // Persona 5 "010028600EBDA000", // Mario 3D World ]; - + + public GraphicsBackend Backend => + EmbeddedWindow switch + { + EmbeddedWindowVulkan => GraphicsBackend.Vulkan, + EmbeddedWindowOpenGL => GraphicsBackend.OpenGl, + EmbeddedWindowMetal => GraphicsBackend.Metal, + _ => throw new NotImplementedException() + }; + public RendererHost(string titleId) { InitializeComponent(); -- 2.47.1 From 61303032f12ca7966339130386d1f5427683d3b6 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Tue, 24 Dec 2024 00:31:49 -0600 Subject: [PATCH 368/368] Silence compile warnings --- src/Ryujinx/AppHost.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 768e70f3c..abcc357a6 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -915,17 +915,20 @@ namespace Ryujinx.Ava IRenderer renderer; GraphicsBackend backend = ConfigurationState.Instance.Graphics.GraphicsBackend; - if (backend == GraphicsBackend.Vulkan || (backend == GraphicsBackend.Auto && !ShouldInitMetal)) + if (ShouldInitMetal) + { +#pragma warning disable CA1416 This call site is reachable on all platforms + // The condition does a check for Mac, on top of checking if it's an ARM Mac. This isn't a problem. + renderer = new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal)!.CreateSurface); +#pragma warning restore CA1416 + } + else if (backend == GraphicsBackend.Vulkan || (backend == GraphicsBackend.Auto && !ShouldInitMetal)) { renderer = VulkanRenderer.Create( ConfigurationState.Instance.Graphics.PreferredGpu, (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface, VulkanHelper.GetRequiredInstanceExtensions); } - else if (ShouldInitMetal) - { - renderer = new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal).CreateSurface); - } else { renderer = new OpenGLRenderer(); -- 2.47.1