From d70b5b29dd7094ea4e7cfd2a1d618d2e0fd71168 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 31 Aug 2024 22:42:56 +0200 Subject: [PATCH 001/368] Start Metal Backend Revert build yml changes --- Directory.Packages.props | 1 + Ryujinx.sln | 8 + .../Configuration/GraphicsBackend.cs | 1 + src/Ryujinx.Graphics.Metal/Constants.cs | 13 + src/Ryujinx.Graphics.Metal/EnumConversion.cs | 192 +++ .../FormatCapabilities.cs | 14 + src/Ryujinx.Graphics.Metal/FormatTable.cs | 172 +++ src/Ryujinx.Graphics.Metal/HardwareInfo.cs | 82 ++ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 246 ++++ src/Ryujinx.Graphics.Metal/Pipeline.cs | 314 +++++ .../Ryujinx.Graphics.Metal.csproj | 17 + src/Ryujinx.Graphics.Metal/TextureView.cs | 77 ++ src/Ryujinx.Graphics.Metal/Window.cs | 53 + .../CodeGen/Msl/CodeGenContext.cs | 88 ++ .../CodeGen/Msl/Declarations.cs | 16 + .../CodeGen/Msl/MslGenerator.cs | 17 + .../Translation/TargetApi.cs | 1 + .../Translation/TargetLanguage.cs | 2 +- src/Ryujinx/AppHost.cs | 5 +- src/Ryujinx/AppHost.cs.orig | 1225 +++++++++++++++++ src/Ryujinx/Program.cs | 4 + .../UI/Renderer/EmbeddedWindowMetal.cs | 25 + .../UI/ViewModels/SettingsViewModel.cs | 2 + .../Views/Settings/SettingsGraphicsView.axaml | 3 + 24 files changed, 2576 insertions(+), 2 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Constants.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/TextureView.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/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 301024cf8..905f7847d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,6 +38,7 @@ + diff --git a/Ryujinx.sln b/Ryujinx.sln index 76ebd573f..9b6d3ce58 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -88,6 +88,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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -255,6 +259,10 @@ Global {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = 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/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs new file mode 100644 index 000000000..120c2d71e --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -0,0 +1,192 @@ +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) + }; + } + + 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..0d8fd1f92 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -0,0 +1,246 @@ +using Ryujinx.Common.Configuration; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader.Translation; +using System; +using SharpMetal; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public sealed class MetalRenderer : IRenderer + { + private MTLDevice _device; + + private Window _window; + + private Pipeline _pipeline; + + internal MTLCommandQueue Queue { get; private set; } + + internal int BufferCount { get; private set; } + + public event EventHandler ScreenCaptured; + public bool PreferThreading => true; + public IPipeline Pipeline => _pipeline; + public IWindow Window => _window; + + public void Initialize(GraphicsDebugLevel logLevel) + { + _device = MTLDevice.MTLCreateSystemDefaultDevice(); + Queue = _device.NewCommandQueueWithMaxCommandBufferCount(Constants.MaxCommandBuffersPerQueue); + + var commandBuffer = Queue.CommandBufferWithDescriptor(new MTLCommandBufferDescriptor { RetainedReferences = true }); + + _pipeline = new Pipeline(_device, commandBuffer); + } + + 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) + { + BufferCount++; + + var buffer = _device.NewBufferWithBytesLengthOptions(pointer, (ulong)size, MTLResourceOptions.StorageModeShared); + var bufferPtr = buffer.NativePtr; + return Unsafe.As(ref bufferPtr); + } + + public BufferHandle CreateBuffer(int size, BufferAccess access) + { + BufferCount++; + + var buffer = _device.NewBufferWithLengthOptions((ulong)size, MTLResourceOptions.StorageModeShared); + 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.CreateSamplerState(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() + }); + + throw new NotImplementedException(); + } + + public ITexture CreateTexture(TextureCreateInfo info, float scale) + { + MTLTextureDescriptor descriptor = new() + { + PixelFormat = FormatCapabilities.ConvertToMTLFormat(info.Format), + TextureType = info.Target.Convert(), + Width = (ulong)info.Width, + Height = (ulong)info.Height, + MipmapLevelCount = (ulong)info.Levels, + SampleCount = (ulong)info.Samples, + }; + + return CreateTextureView(info, scale); + } + + internal TextureView CreateTextureView(TextureCreateInfo info, float scale) + { + throw new NotImplementedException(); + } + + 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) + { + throw new NotImplementedException(); + } + + public PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) + { + throw new NotImplementedException(); + } + + 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, + supportsImageLoadFormatted: false, + supportsLayerVertexTessellation: false, + supportsMismatchingViewFormat: true, + supportsCubemapView: true, + supportsNonConstantTextureOffset: false, + supportsShaderBallot: false, + supportsTextureShadowLod: false, + supportsViewportIndexVertexTessellation: false, + supportsViewportMask: false, + supportsViewportSwizzle: false, + supportsIndirectParameters: true, + 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 void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) + { + throw new NotImplementedException(); + } + + public void UpdateCounters() + { + throw new NotImplementedException(); + } + + public void PreFrame() + { + throw new NotImplementedException(); + } + + public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, bool hostReserved) + { + throw new NotImplementedException(); + } + + public void ResetCounter(CounterType type) + { + throw new NotImplementedException(); + } + + 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(); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs new file mode 100644 index 000000000..c703a01a1 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -0,0 +1,314 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using System; +using SharpMetal; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public class Pipeline : IPipeline, IDisposable + { + private MTLCommandBuffer _commandBuffer; + private MTLRenderCommandEncoder _renderCommandEncoder; + + public Pipeline(MTLDevice device, MTLCommandBuffer commandBuffer) + { + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + var renderPipelineState = device.CreateRenderPipelineState(renderPipelineDescriptor, out NSError _); + + _commandBuffer = commandBuffer; + _renderCommandEncoder = _commandBuffer.CreateRenderCommandEncoder(new MTLRenderPassDescriptor()); + _renderCommandEncoder.SetRenderPipelineState(renderPipelineState); + } + + 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() + { + throw new NotImplementedException(); + } + + 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) + { + throw new NotImplementedException(); + } + + public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) + { + throw new NotImplementedException(); + } + + 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) + { + throw new NotImplementedException(); + } + + 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) + { + throw new NotImplementedException(); + } + + public void SetFrontFace(FrontFace frontFace) + { + throw new NotImplementedException(); + } + + public void SetIndexBuffer(BufferRange buffer, IndexType type) + { + throw new NotImplementedException(); + } + + public void SetImage(int binding, ITexture texture, Format imageFormat) + { + throw new NotImplementedException(); + } + + public void SetLineParameters(float width, bool smooth) + { + throw new NotImplementedException(); + } + + public void SetLogicOpState(bool enable, LogicalOp op) + { + throw new NotImplementedException(); + } + + 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) + { + throw new NotImplementedException(); + } + + public void SetPrimitiveRestart(bool enable, int index) + { + throw new NotImplementedException(); + } + + public void SetPrimitiveTopology(PrimitiveTopology topology) + { + throw new NotImplementedException(); + } + + public void SetProgram(IProgram program) + { + throw new NotImplementedException(); + } + + public void SetRasterizerDiscard(bool discard) + { + throw new NotImplementedException(); + } + + public void SetRenderTargetScale(float scale) + { + throw new NotImplementedException(); + } + + public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) + { + throw new NotImplementedException(); + } + + public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + { + throw new NotImplementedException(); + } + + public void SetScissors(ReadOnlySpan> regions) + { + throw new NotImplementedException(); + } + + public void SetStencilTest(StencilTestDescriptor stencilTest) + { + throw new NotImplementedException(); + } + + 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 void SetViewports(ReadOnlySpan viewports, bool disableTransform) + { + throw new NotImplementedException(); + } + + public void TextureBarrier() + { + throw new NotImplementedException(); + } + + public void TextureBarrierTiled() + { + throw new NotImplementedException(); + } + + public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) + { + throw new NotImplementedException(); + } + + public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) + { + throw new NotImplementedException(); + } + + public void EndHostConditionalRendering() + { + throw new NotImplementedException(); + } + + public void UpdateRenderScale(ReadOnlySpan scales, int totalCount, int fragmentCount) + { + throw new NotImplementedException(); + } + + public void BeginTransformFeedback(PrimitiveTopology topology) + { + // Metal does not support Transform Feedback + throw new NotSupportedException(); + } + + public void EndTransformFeedback() + { + // Metal does not support Transform Feedback + throw new NotSupportedException(); + } + + public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) + { + // Metal does not support Transform Feedback + throw new NotSupportedException(); + } + + public void Dispose() + { + + } + } +} \ No newline at end of file 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/TextureView.cs b/src/Ryujinx.Graphics.Metal/TextureView.cs new file mode 100644 index 000000000..0109a4f7e --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/TextureView.cs @@ -0,0 +1,77 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.Metal +{ + class TextureView : ITexture, IDisposable + { + public int Width { get; } + public int Height { get; } + public float ScaleFactor { get; } + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + throw new NotImplementedException(); + } + + public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + throw new NotImplementedException(); + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + throw new NotImplementedException(); + } + + 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 void SetData(SpanOrArray data, int layer, int level, Rectangle region) + { + throw new NotImplementedException(); + } + + 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..59122a493 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -0,0 +1,53 @@ +using Ryujinx.Graphics.GAL; +using System; +using SharpMetal; + +namespace Ryujinx.Graphics.Metal +{ + public class Window : IWindow, IDisposable + { + + public Window() + { + /*var viewport = new MTLViewport + { + + };*/ + } + + public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + { + throw new NotImplementedException(); + } + + 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() + { + + } + } +} \ 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 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/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 f4bfd1169..764230855 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -28,6 +28,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; @@ -1067,10 +1068,12 @@ namespace Ryujinx.Ava 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} %")); + $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", + $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); } public async Task ShowExitPrompt() 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 6c83cedcf..a5890dc08 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -188,6 +188,10 @@ namespace Ryujinx.Ava { ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; } + else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "metal") + { + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Metal; + } } // Check if docked mode was overriden. 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 70e5fa5c7..75c507247 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -111,6 +111,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 0a12575ad..da9cdee2e 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -43,6 +43,9 @@ + + + From a7101a778ed4b4ed398f19f589e3e4589c2e067f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 27 Jul 2023 21:51:20 -0400 Subject: [PATCH 002/368] Texture, Pipeline, Sample, Renderer Improvements --- src/Ryujinx.Graphics.Metal/EnumConversion.cs | 14 ++ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 30 ++-- src/Ryujinx.Graphics.Metal/Pipeline.cs | 134 +++++++++++++----- src/Ryujinx.Graphics.Metal/Sampler.cs | 19 +++ .../{TextureView.cs => Texture.cs} | 38 ++++- 5 files changed, 181 insertions(+), 54 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/Sampler.cs rename src/Ryujinx.Graphics.Metal/{TextureView.cs => Texture.cs} (60%) diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs index 120c2d71e..0e23d8804 100644 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Metal/EnumConversion.cs @@ -182,6 +182,20 @@ namespace Ryujinx.Graphics.Metal }; } + 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}."); diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 0d8fd1f92..48ddfba7a 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -1,8 +1,8 @@ using Ryujinx.Common.Configuration; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader.Translation; +using SharpMetal.Metal; using System; -using SharpMetal; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -28,10 +28,10 @@ namespace Ryujinx.Graphics.Metal public void Initialize(GraphicsDebugLevel logLevel) { - _device = MTLDevice.MTLCreateSystemDefaultDevice(); - Queue = _device.NewCommandQueueWithMaxCommandBufferCount(Constants.MaxCommandBuffersPerQueue); + _device = MTLDevice.CreateSystemDefaultDevice(); + Queue = _device.NewCommandQueue(); - var commandBuffer = Queue.CommandBufferWithDescriptor(new MTLCommandBufferDescriptor { RetainedReferences = true }); + var commandBuffer = Queue.CommandBuffer(); _pipeline = new Pipeline(_device, commandBuffer); } @@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Metal { BufferCount++; - var buffer = _device.NewBufferWithBytesLengthOptions(pointer, (ulong)size, MTLResourceOptions.StorageModeShared); + var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); var bufferPtr = buffer.NativePtr; return Unsafe.As(ref bufferPtr); } @@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Metal { BufferCount++; - var buffer = _device.NewBufferWithLengthOptions((ulong)size, MTLResourceOptions.StorageModeShared); + var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); var bufferPtr = buffer.NativePtr; return Unsafe.As(ref bufferPtr); } @@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.Metal { (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); - var sampler = _device.CreateSamplerState(new MTLSamplerDescriptor + var sampler = _device.NewSamplerState(new MTLSamplerDescriptor { BorderColor = MTLSamplerBorderColor.TransparentBlack, MinFilter = minFilter, @@ -90,10 +90,10 @@ namespace Ryujinx.Graphics.Metal RAddressMode = info.AddressP.Convert() }); - throw new NotImplementedException(); + return new Sampler(sampler); } - public ITexture CreateTexture(TextureCreateInfo info, float scale) + public ITexture CreateTexture(TextureCreateInfo info) { MTLTextureDescriptor descriptor = new() { @@ -105,10 +105,10 @@ namespace Ryujinx.Graphics.Metal SampleCount = (ulong)info.Samples, }; - return CreateTextureView(info, scale); + return CreateTextureView(info); } - internal TextureView CreateTextureView(TextureCreateInfo info, float scale) + internal Texture CreateTextureView(TextureCreateInfo info) { throw new NotImplementedException(); } @@ -160,17 +160,21 @@ namespace Ryujinx.Graphics.Metal 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, @@ -212,7 +216,7 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, bool hostReserved) + public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, float divisor, bool hostReserved) { throw new NotImplementedException(); } @@ -243,4 +247,4 @@ namespace Ryujinx.Graphics.Metal _pipeline.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index c703a01a1..ff111a2ec 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -1,7 +1,9 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; +using SharpMetal.Foundation; +using SharpMetal.Metal; using System; -using SharpMetal; +using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -9,16 +11,30 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public class Pipeline : IPipeline, IDisposable { + private MTLDevice _device; private MTLCommandBuffer _commandBuffer; private MTLRenderCommandEncoder _renderCommandEncoder; + private PrimitiveTopology _topology; + + private MTLBuffer _indexBuffer; + private MTLIndexType _indexType; + private ulong _indexBufferOffset; + public Pipeline(MTLDevice device, MTLCommandBuffer commandBuffer) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); - var renderPipelineState = device.CreateRenderPipelineState(renderPipelineDescriptor, out NSError _); + var error = new NSError(IntPtr.Zero); + _device = device; + var 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!"); + } _commandBuffer = commandBuffer; - _renderCommandEncoder = _commandBuffer.CreateRenderCommandEncoder(new MTLRenderPassDescriptor()); + _renderCommandEncoder = _commandBuffer.RenderCommandEncoder(new MTLRenderPassDescriptor()); _renderCommandEncoder.SetRenderPipelineState(renderPipelineState); } @@ -45,7 +61,7 @@ namespace Ryujinx.Graphics.Metal public void CommandBufferBarrier() { - throw new NotImplementedException(); + // TODO: Only required for MTLHeap or untracked resources } public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) @@ -60,12 +76,18 @@ namespace Ryujinx.Graphics.Metal public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { - throw new NotImplementedException(); + // 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) { - throw new NotImplementedException(); + // 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) @@ -95,7 +117,7 @@ namespace Ryujinx.Graphics.Metal public void SetAlphaTest(bool enable, float reference, CompareOp op) { - throw new NotImplementedException(); + // Metal does not support alpha test. } public void SetBlendState(AdvancedBlendDescriptor blend) @@ -130,17 +152,23 @@ namespace Ryujinx.Graphics.Metal public void SetFaceCulling(bool enable, Face face) { - throw new NotImplementedException(); + _renderCommandEncoder.SetCullMode(enable ? face.Convert() : MTLCullMode.None); } public void SetFrontFace(FrontFace frontFace) { - throw new NotImplementedException(); + _renderCommandEncoder.SetFrontFacingWinding(frontFace.Convert()); } public void SetIndexBuffer(BufferRange buffer, IndexType type) { - throw new NotImplementedException(); + 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) @@ -150,12 +178,12 @@ namespace Ryujinx.Graphics.Metal public void SetLineParameters(float width, bool smooth) { - throw new NotImplementedException(); + // Not supported in Metal } public void SetLogicOpState(bool enable, LogicalOp op) { - throw new NotImplementedException(); + // Not supported in Metal } public void SetMultisampleState(MultisampleDescriptor multisample) @@ -175,17 +203,20 @@ namespace Ryujinx.Graphics.Metal public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) { - throw new NotImplementedException(); + // Not supported in Metal } public void SetPrimitiveRestart(bool enable, int index) { - throw new NotImplementedException(); + // 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) { - throw new NotImplementedException(); + _topology = topology; } public void SetProgram(IProgram program) @@ -198,11 +229,6 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void SetRenderTargetScale(float scale) - { - throw new NotImplementedException(); - } - public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) { throw new NotImplementedException(); @@ -213,14 +239,33 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void SetScissors(ReadOnlySpan> regions) + public unsafe void SetScissors(ReadOnlySpan> regions) { - throw new NotImplementedException(); + // 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) { - throw new NotImplementedException(); + // TODO } public void SetStorageBuffers(ReadOnlySpan buffers) @@ -253,9 +298,30 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void SetViewports(ReadOnlySpan viewports, bool disableTransform) + public unsafe void SetViewports(ReadOnlySpan viewports) { - throw new NotImplementedException(); + // 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() @@ -270,40 +336,34 @@ namespace Ryujinx.Graphics.Metal public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) { - throw new NotImplementedException(); + // TODO: Implementable via indirect draw commands + return false; } public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) { - throw new NotImplementedException(); + // TODO: Implementable via indirect draw commands + return false; } public void EndHostConditionalRendering() { - throw new NotImplementedException(); - } - - public void UpdateRenderScale(ReadOnlySpan scales, int totalCount, int fragmentCount) - { - throw new NotImplementedException(); + // TODO: Implementable via indirect draw commands } public void BeginTransformFeedback(PrimitiveTopology topology) { // Metal does not support Transform Feedback - throw new NotSupportedException(); } public void EndTransformFeedback() { // Metal does not support Transform Feedback - throw new NotSupportedException(); } public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) { // Metal does not support Transform Feedback - throw new NotSupportedException(); } public void Dispose() @@ -311,4 +371,4 @@ namespace Ryujinx.Graphics.Metal } } -} \ 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/TextureView.cs b/src/Ryujinx.Graphics.Metal/Texture.cs similarity index 60% rename from src/Ryujinx.Graphics.Metal/TextureView.cs rename to src/Ryujinx.Graphics.Metal/Texture.cs index 0109a4f7e..064d6bd1a 100644 --- a/src/Ryujinx.Graphics.Metal/TextureView.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -1,14 +1,44 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; using System; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - class TextureView : ITexture, IDisposable + [SupportedOSPlatform("macos")] + class Texture : ITexture, IDisposable { - public int Width { get; } - public int Height { get; } - public float ScaleFactor { get; } + private readonly TextureCreateInfo _info; + + public MTLTexture MTLTexture; + public TextureCreateInfo Info => Info; + public int Width => Info.Width; + public int Height => Info.Height; + + public Texture(MTLDevice device, TextureCreateInfo info, int firstLayer, int firstLevel) + { + _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.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) { throw new NotImplementedException(); From 2cea603124bade86e6ee111b424445f79a1d7ca5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 27 Jul 2023 22:54:24 -0400 Subject: [PATCH 003/368] Texture Copys --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 4 +-- src/Ryujinx.Graphics.Metal/Pipeline.cs | 40 +++++++++++++++++---- src/Ryujinx.Graphics.Metal/Texture.cs | 31 ++++++++++++++-- src/Ryujinx.Graphics.Metal/Window.cs | 18 +++++----- 4 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 48ddfba7a..b08e23b80 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -31,9 +31,7 @@ namespace Ryujinx.Graphics.Metal _device = MTLDevice.CreateSystemDefaultDevice(); Queue = _device.NewCommandQueue(); - var commandBuffer = Queue.CommandBuffer(); - - _pipeline = new Pipeline(_device, commandBuffer); + _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 ff111a2ec..dd1a5e071 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -11,9 +11,16 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public class Pipeline : IPipeline, IDisposable { - private MTLDevice _device; + 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; @@ -21,21 +28,42 @@ namespace Ryujinx.Graphics.Metal private MTLIndexType _indexType; private ulong _indexBufferOffset; - public Pipeline(MTLDevice device, MTLCommandBuffer commandBuffer) + public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { + _device = device; + _mtlCommandQueue = commandQueue; + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); var error = new NSError(IntPtr.Zero); - _device = device; - var renderPipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + _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!"); } - _commandBuffer = commandBuffer; + 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); + _renderCommandEncoder.SetRenderPipelineState(_renderPipelineState); + + _blitCommandEncoder = _commandBuffer.BlitCommandEncoder(new MTLBlitPassDescriptor()); } public void Barrier() diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 064d6bd1a..a2c2443b4 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -10,13 +10,14 @@ namespace Ryujinx.Graphics.Metal class Texture : ITexture, IDisposable { private readonly TextureCreateInfo _info; + private readonly Pipeline _pipeline; public MTLTexture MTLTexture; public TextureCreateInfo Info => Info; public int Width => Info.Width; public int Height => Info.Height; - public Texture(MTLDevice device, TextureCreateInfo info, int firstLayer, int firstLevel) + public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, int firstLayer, int firstLevel) { _info = info; @@ -27,6 +28,7 @@ namespace Ryujinx.Graphics.Metal 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 { @@ -37,16 +39,39 @@ namespace Ryujinx.Graphics.Metal }; MTLTexture = device.NewTexture(descriptor); + _pipeline = pipeline; } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { - throw new NotImplementedException(); + 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) { - throw new NotImplementedException(); + 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) diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 59122a493..31c534958 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -1,23 +1,25 @@ using Ryujinx.Graphics.GAL; using System; -using SharpMetal; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { + [SupportedOSPlatform("macos")] public class Window : IWindow, IDisposable { + private readonly MetalRenderer _renderer; - public Window() + public Window(MetalRenderer renderer) { - /*var viewport = new MTLViewport - { - - };*/ + _renderer = renderer; } public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { - throw new NotImplementedException(); + if (_renderer.Pipeline is Pipeline pipeline) + { + pipeline.Present(); + } } public void SetSize(int width, int height) @@ -50,4 +52,4 @@ namespace Ryujinx.Graphics.Metal } } -} \ No newline at end of file +} From eaa04a206a6277d8b615c4e8d0071236e59854a0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 27 Jul 2023 23:36:04 -0400 Subject: [PATCH 004/368] Renderer cleanup --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 47 +++++++++++---------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index b08e23b80..6e157e017 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -1,6 +1,7 @@ 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; @@ -11,27 +12,26 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public sealed class MetalRenderer : IRenderer { - private MTLDevice _device; - - private Window _window; - - private Pipeline _pipeline; - - internal MTLCommandQueue Queue { get; private set; } - - internal int BufferCount { get; private set; } + 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 void Initialize(GraphicsDebugLevel logLevel) + public MetalRenderer() { _device = MTLDevice.CreateSystemDefaultDevice(); - Queue = _device.NewCommandQueue(); + _queue = _device.NewCommandQueue(); + _pipeline = new Pipeline(_device, _queue); + _window = new Window(this); + } - _pipeline = new Pipeline(_device, Queue); + public void Initialize(GraphicsDebugLevel logLevel) + { } public void BackgroundContextAction(Action action, bool alwaysBackground = false) @@ -46,8 +46,6 @@ namespace Ryujinx.Graphics.Metal public BufferHandle CreateBuffer(IntPtr pointer, int size) { - BufferCount++; - var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); var bufferPtr = buffer.NativePtr; return Unsafe.As(ref bufferPtr); @@ -55,8 +53,6 @@ namespace Ryujinx.Graphics.Metal public BufferHandle CreateBuffer(int size, BufferAccess access) { - BufferCount++; - var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); var bufferPtr = buffer.NativePtr; return Unsafe.As(ref bufferPtr); @@ -199,29 +195,36 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) + public unsafe void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) { - throw new NotImplementedException(); + 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() { - throw new NotImplementedException(); + // 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() { - throw new NotImplementedException(); + } public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, float divisor, bool hostReserved) { - throw new NotImplementedException(); + // 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 ResetCounter(CounterType type) { - throw new NotImplementedException(); + // 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) From 190311d4b0d0e3a7559bec0628eeaea2338d03dc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 27 Jul 2023 23:37:03 -0400 Subject: [PATCH 005/368] Fix error --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 6e157e017..e0074351e 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -220,6 +220,7 @@ namespace Ryujinx.Graphics.Metal 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 + throw new NotImplementedException(); } public void ResetCounter(CounterType type) From 9b994b52e94fc52fcc4e72b5fa1a379049f7cdf2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 00:29:05 -0400 Subject: [PATCH 006/368] IoMap --- .../CodeGen/Msl/Instructions/IoMap.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs 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), + }; + } + } +} From c97cac3e537c8c8edf91c2ffea1b936c7fad9913 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 08:20:15 -0400 Subject: [PATCH 007/368] Start Texture region-based CopyTo --- src/Ryujinx.Graphics.Metal/Texture.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index a2c2443b4..9d7e5e0d0 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Metal { private readonly TextureCreateInfo _info; private readonly Pipeline _pipeline; + private readonly MTLDevice _device; public MTLTexture MTLTexture; public TextureCreateInfo Info => Info; @@ -19,6 +20,8 @@ namespace Ryujinx.Graphics.Metal public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, int firstLayer, int firstLevel) { + _device = device; + _pipeline = pipeline; _info = info; var descriptor = new MTLTextureDescriptor(); @@ -38,8 +41,7 @@ namespace Ryujinx.Graphics.Metal alpha = Info.SwizzleA.Convert() }; - MTLTexture = device.NewTexture(descriptor); - _pipeline = pipeline; + MTLTexture = _device.NewTexture(descriptor); } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) @@ -76,7 +78,10 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { - throw new NotImplementedException(); + 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) From db3c60f91f61313a128083896d7afd57508a74ac Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 08:31:46 -0400 Subject: [PATCH 008/368] Bump SharpMetal --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 905f7847d..5682c48c1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,7 +38,7 @@ - + From 964233d458a74df8a369b2966de494bb27985083 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 11:55:52 -0400 Subject: [PATCH 009/368] Delete and Get Data from Buffer --- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 25 +++++---------------- src/Ryujinx.Graphics.Metal/Texture.cs | 20 ++++++++++++++--- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index e0074351e..1c4db1a01 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -89,22 +89,7 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateTexture(TextureCreateInfo info) { - MTLTextureDescriptor descriptor = new() - { - PixelFormat = FormatCapabilities.ConvertToMTLFormat(info.Format), - TextureType = info.Target.Convert(), - Width = (ulong)info.Width, - Height = (ulong)info.Height, - MipmapLevelCount = (ulong)info.Levels, - SampleCount = (ulong)info.Samples, - }; - - return CreateTextureView(info); - } - - internal Texture CreateTextureView(TextureCreateInfo info) - { - throw new NotImplementedException(); + return new Texture(_device, _pipeline, info); } public bool PrepareHostMapping(IntPtr address, ulong size) @@ -120,12 +105,14 @@ namespace Ryujinx.Graphics.Metal public void DeleteBuffer(BufferHandle buffer) { - throw new NotImplementedException(); + MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); + mtlBuffer.SetPurgeableState(MTLPurgeableState.Empty); } - public PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) + public unsafe PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) { - throw new NotImplementedException(); + MTLBuffer mtlBuffer = new(Unsafe.As(ref buffer)); + return new PinnedSpan(IntPtr.Add(mtlBuffer.Contents, offset).ToPointer(), size); } public Capabilities GetCapabilities() diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 9d7e5e0d0..b03fb26c8 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Metal public int Width => Info.Width; public int Height => Info.Height; - public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info, int firstLayer, int firstLevel) + public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) { _device = device; _pipeline = pipeline; @@ -114,9 +114,23 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void SetData(SpanOrArray data, int layer, int level, Rectangle region) + public unsafe void SetData(SpanOrArray data, int layer, int level, Rectangle region) { - throw new NotImplementedException(); + // 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) From 93054ce3dc37f40c5eed15d17e31975102878d56 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 11:59:07 -0400 Subject: [PATCH 010/368] BufferAccess --- 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 1c4db1a01..787f01c6e 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -54,6 +54,12 @@ 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); + } + var bufferPtr = buffer.NativePtr; return Unsafe.As(ref bufferPtr); } From 50cc73a9b24bd76b13a7b733183120bd28e110cd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 13:29:26 -0400 Subject: [PATCH 011/368] Spoof Counters --- src/Ryujinx.Graphics.Metal/CounterEvent.cs | 23 +++++++++++++++++++++ src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 4 +++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/Ryujinx.Graphics.Metal/CounterEvent.cs 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/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 787f01c6e..dc6b3d05a 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -213,7 +213,9 @@ namespace Ryujinx.Graphics.Metal 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 - throw new NotImplementedException(); + var counterEvent = new CounterEvent(); + resultHandler?.Invoke(counterEvent, type == CounterType.SamplesPassed ? (ulong)1 : 0); + return counterEvent; } public void ResetCounter(CounterType type) From b943370602c14d5ab7912b63d8ba405dae56e56d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 15:04:35 -0400 Subject: [PATCH 012/368] Finish SetData /w region --- src/Ryujinx.Graphics.Metal/Texture.cs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index b03fb26c8..23a248622 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; @@ -114,22 +114,28 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - 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; + ulong bytesPerRow = (ulong)(Info.Width * Info.BytesPerPixel); + ulong bytesPerImage = 0; + + if (MTLTexture.TextureType == MTLTextureType.Type3D) + { + bytesPerImage = bytesPerRow * (ulong)Info.Height; + } + 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) + unsafe { - MTLTexture.ReplaceRegion(mtlRegion, (ulong)level, (ulong)layer, new IntPtr(pData), bytesPerRow, 0); + fixed (byte* pData = data.Span) + { + MTLTexture.ReplaceRegion(mtlRegion, (ulong)level, (ulong)layer, new IntPtr(pData), bytesPerRow, bytesPerImage); + } } } From 14981b477ded96a2dd9e660f2197ead3547e5b89 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 15:09:40 -0400 Subject: [PATCH 013/368] Fix byte alignment --- 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 23a248622..7dd73ae5b 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { - ulong bytesPerRow = (ulong)(Info.Width * Info.BytesPerPixel); + ulong bytesPerRow = (ulong)Info.GetMipStride(level); ulong bytesPerImage = 0; if (MTLTexture.TextureType == MTLTextureType.Type3D) From a9d6228ec4e7162c3a7b5ba73435319538a4f96d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 16:23:13 -0400 Subject: [PATCH 014/368] One encoder at a time --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 113 +++++++++++++----- .../RenderEncoderState.cs | 27 +++++ src/Ryujinx.Graphics.Metal/Texture.cs | 26 +++- 3 files changed, 135 insertions(+), 31 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/RenderEncoderState.cs diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index dd1a5e071..b0be094d8 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -15,14 +15,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 +32,57 @@ 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!"); } - 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() @@ -104,18 +123,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 +221,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 +299,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/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 7dd73ae5b..177582a2b 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -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, From 41d5d1849d7ec3a7c0d9807523a523cbae4c1989 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 16:39:14 -0400 Subject: [PATCH 015/368] Use Ryujinx Logger --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 4 +-- src/Ryujinx.Graphics.Metal/StringHelper.cs | 30 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) 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 b0be094d8..f45eba34c 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; @@ -35,8 +36,7 @@ namespace Ryujinx.Graphics.Metal _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)}"); } _commandBuffer = _mtlCommandQueue.CommandBuffer(); 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); + } + } +} From 960a1a8209952d67e2233163a94efc2470969184 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 16:51:07 -0400 Subject: [PATCH 016/368] Clear Buffer --- src/Ryujinx.Graphics.Metal/Pipeline.cs | 22 +++++++++++++++++++++- src/Ryujinx.Graphics.Metal/Texture.cs | 1 + 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index f45eba34c..a0af3c33f 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -92,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) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 177582a2b..1372ba551 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -136,6 +136,7 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } + // TODO: Rewrite using MTLBlitCommandEncoder public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { ulong bytesPerRow = (ulong)Info.GetMipStride(level); From 1b4cb49cddf26fc21c41552b3a454d7535ec85bb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 23:33:09 -0400 Subject: [PATCH 017/368] Rewrite SetData for GPU --- src/Ryujinx.Graphics.Metal/Texture.cs | 39 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 1372ba551..94a4187bb 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -136,29 +136,44 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - // TODO: Rewrite using MTLBlitCommandEncoder public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { + 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 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 }, - }; - unsafe { - fixed (byte* pData = data.Span) - { - MTLTexture.ReplaceRegion(mtlRegion, (ulong)level, (ulong)layer, new IntPtr(pData), bytesPerRow, bytesPerImage); - } + 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 } + ); } } From 2ccd45936e6a51e2e2206a51e6a1c3578fa3d83e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 23:35:55 -0400 Subject: [PATCH 018/368] SetData without region --- src/Ryujinx.Graphics.Metal/Texture.cs | 38 ++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 94a4187bb..c976dba6d 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -133,7 +133,43 @@ 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 void SetData(SpanOrArray data, int layer, int level, Rectangle region) From 9a267249298745dbc3c4c1b9e3a4ec34971c04aa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 23:50:00 -0400 Subject: [PATCH 019/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, From 44cdeacd12032dd543b86cedbcbf413743f3edd8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jul 2023 23:56:33 -0400 Subject: [PATCH 020/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) From daff3c4598172cb8b94c6304a8fe619e2fa0389e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 00:07:54 -0400 Subject: [PATCH 021/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; From c376395b24bf0cdf5c14b77fe8165ef678eb78c3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 00:14:21 -0400 Subject: [PATCH 022/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; From d20878124d558f337f21b9aef0cbeb733a4124d0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 00:30:08 -0400 Subject: [PATCH 023/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); } } } From fda112a8ceb6da6d158ded9b63ade0ff0bf6ffd9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 00:46:13 -0400 Subject: [PATCH 024/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 + }); } } } From ce8c2aaf5c819146637dfb6092b55ea158bd212e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 01:18:51 -0400 Subject: [PATCH 025/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(); From 73b1f9a316102a5f8ca94b445921506994d982fe Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jul 2023 01:20:15 -0400 Subject: [PATCH 026/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(); } From 516be49f3ae1d5b1d2a6861ec330e43fc69f5f50 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 31 Jul 2023 17:44:01 -0400 Subject: [PATCH 027/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, From 52a48606b445a6cb27f983111c14bb5b3dc6f732 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 1 Aug 2023 22:36:07 -0400 Subject: [PATCH 028/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() From acfb22e58c2b9bc4233b56de30ef006278b1833f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 15:57:57 -0400 Subject: [PATCH 029/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) From c0bffaf5472d4eea4d408f0cf45dabab5e016b53 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 19:56:59 -0400 Subject: [PATCH 030/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) From b8ac60e00c61015490aabeef99c2a5aed3fbfaa7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 20:32:59 -0400 Subject: [PATCH 031/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); } } From e8ebda10ef7cb5e2b9a917758cbe4f8f57ced23c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 21:18:28 -0400 Subject: [PATCH 032/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) From 1da7e7bc7091673d09751f1b67fdf2b1d9e8c408 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 21:32:41 -0400 Subject: [PATCH 033/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) From fd37d100f8a2e176e36750167fac861854d67d49 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 2 Aug 2023 21:53:49 -0400 Subject: [PATCH 034/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[] {}; + } + } +} From 30e43b58b57d627c769d420f771348431f08927e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 08:48:41 -0400 Subject: [PATCH 035/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 From 6b264ec9f7f09b3bacafe8a89b924b4007572105 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 08:50:36 -0400 Subject: [PATCH 036/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), From 288cc5bc04b0ea7aae818c6dd4de3ec4c1193ae2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 08:58:14 -0400 Subject: [PATCH 037/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(); From 9bff5bcaf6b878ecfaba1634bb38b0b8d7008893 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 09:21:32 -0400 Subject: [PATCH 038/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); From da7f3e128f71e48ce08d2ed7540f1d22d4305a48 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 11:51:11 -0400 Subject: [PATCH 039/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, + } +} From 222aded39bfbffb0393032ff1ff3443475cea3e7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 14:50:49 -0400 Subject: [PATCH 040/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 | 4 ++ src/Ryujinx/AppHost.cs.orig | 11 +-- 15 files changed, 156 insertions(+), 83 deletions(-) create mode 100644 Directory.Packages.props.orig delete mode 100644 src/Ryujinx.Graphics.Metal/FormatCapabilities.cs 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 764230855..8566afb20 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -830,6 +830,10 @@ namespace Ryujinx.Ava VulkanHelper.GetRequiredInstanceExtensions, ConfigurationState.Instance.Graphics.PreferredGpu.Value); } + else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal && OperatingSystem.IsMacOS()) + { + renderer = new MetalRenderer(); + } else { renderer = new OpenGLRenderer(); diff --git a/src/Ryujinx/AppHost.cs.orig b/src/Ryujinx/AppHost.cs.orig index 99663fbc5..302ef914c 100644 --- a/src/Ryujinx/AppHost.cs.orig +++ b/src/Ryujinx/AppHost.cs.orig @@ -760,10 +760,13 @@ namespace Ryujinx.Ava VulkanHelper.GetRequiredInstanceExtensions, ConfigurationState.Instance.Graphics.PreferredGpu.Value); } - else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal) +<<<<<<< HEAD +======= + else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal && OperatingSystem.IsMacOS()) { renderer = new MetalRenderer(); } +>>>>>>> b83dc41f8 (Formatting) else { renderer = new OpenGLRenderer(); @@ -1002,14 +1005,12 @@ namespace Ryujinx.Ava 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} %")); + $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", + $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); } public async Task ShowExitPrompt() From f80b83a3e6d8a47a09f15efcc4c6a60de74eaba3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 16:47:10 -0400 Subject: [PATCH 041/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; From b39bf5ca9403136bf7c064ff9144a819bd092ace Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 16:53:53 -0400 Subject: [PATCH 042/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(); From 9b23487b56d7cf7ddad840facf63e83682cf510d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 17:04:59 -0400 Subject: [PATCH 043/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() From face61951f28b33244e579bb1e7fcf75870d349e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 17:32:04 -0400 Subject: [PATCH 044/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 4ee271203..27b91418d 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -20,6 +20,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; @@ -507,9 +509,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) - : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode); + 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) @@ -541,6 +546,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 610229544..6846a6f7f 100644 --- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -29,6 +29,7 @@ + From ad79f611a9d32c0877dbfd0b8eb842fd4a1f5c6a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 19:01:34 -0400 Subject: [PATCH 045/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; From d2518920772cffa5a1256bcedaae7eff20d921c7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 3 Aug 2023 23:21:22 -0400 Subject: [PATCH 046/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 8ccf5348f..e0a92da5a 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -14,5 +14,8 @@ - + + + + From f92c36c3dd16c78c72698f38baf5ae8a737793a4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 4 Aug 2023 23:51:24 -0400 Subject: [PATCH 047/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() { } From dbdc2d574c7e338cd45a386336d82ce19f7e6033 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 048/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) From fcf3094dfa27ac468d2c44ad78633a9b3110f744 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 12 Aug 2023 15:12:35 +0100 Subject: [PATCH 049/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; } From ee6081757d8c9424a3b696d24cca858ba189ed6a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 14 Aug 2023 12:12:44 +0100 Subject: [PATCH 050/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 From d2cfbebc2ff823ef58799e58fce01305de3a7358 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 14 Aug 2023 12:17:22 +0100 Subject: [PATCH 051/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() From 51b6a156dffa8fe4e3d8883b9b75de6d0a00ae53 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 15 Aug 2023 14:17:00 +0100 Subject: [PATCH 052/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()), }; } From d0f1278806e23037a86a90831adbe76484da91a6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 30 Aug 2023 19:25:12 +0100 Subject: [PATCH 053/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 27b91418d..545a0efcf 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -512,7 +512,9 @@ namespace Ryujinx.Headless.SDL2 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), + GraphicsBackend.Metal => OperatingSystem.IsMacOS() ? + new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode) : + throw new Exception("Attempted to use Metal renderer on non-macOS platform!"), _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode) }; } From 08d10c16c665456bdfeb97dce918fe9f8722254f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 30 Aug 2023 19:35:57 +0100 Subject: [PATCH 054/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 ); } From a19fbe3d5fe8b441aeb2e6d64e2395de4a921aa0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 30 Aug 2023 19:53:19 +0100 Subject: [PATCH 055/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); From e8e7e1b0090dc57e439e63fbac3e7275abdbe93e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 9 Oct 2023 11:12:04 -0400 Subject: [PATCH 056/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; From 7cd5d6aca9fd233828686760e12bc022fde74511 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 9 Oct 2023 11:16:33 -0400 Subject: [PATCH 057/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; From 97d69265fe01e32155cf0827e506381e40d61dcc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 9 Oct 2023 11:20:23 -0400 Subject: [PATCH 058/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); From 983701dd7c671f1cbc9252b14cd8d8fa370b339e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 9 Oct 2023 11:33:28 -0400 Subject: [PATCH 059/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) From f83c75eb7e105e0563141f2bbc45a8d00c767550 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 13:26:49 -0400 Subject: [PATCH 060/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() From a0ebf80fd6eeeeae1ce8e5358b16945470b934ee Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 13:43:57 -0400 Subject: [PATCH 061/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), From a2df7f3fe92834645730c200ab627342ab1456c0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 14:14:28 -0400 Subject: [PATCH 062/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() From 52640171bfd86e72b0519aeb211efdc59072ac92 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 15:26:40 -0400 Subject: [PATCH 063/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; + } + } + } } } From 2051e274eebc3bcc70a65d0e78c3eeb0ca5256ba Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 17:53:51 -0400 Subject: [PATCH 064/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; } } From bb09ab818c0d84c91f0ba3e4287e902ceb739139 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 18:00:32 -0400 Subject: [PATCH 065/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++) { From 91c040d649aef5f0ceab95ae804fbe3e491c413a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 18:04:26 -0400 Subject: [PATCH 066/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; + } } } From 5eff585e8f99e95726ca1ba224413cd716f82226 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 18:36:52 -0400 Subject: [PATCH 067/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; - } - } - } } } From 1ac82d527be772658e9ef1196ed039b82db3ccf6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 19:02:38 -0400 Subject: [PATCH 068/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); } } } From 6e3b317d26c944d5991615ac6fa30b8e2a5ba6f2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 20:00:56 -0400 Subject: [PATCH 069/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) { From cec40e5bd594d689316920507afb713bb646c6b6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 21:56:08 -0400 Subject: [PATCH 070/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) From 54520482923372be51024b1f7e691f1d32a71a1f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 22:03:10 -0400 Subject: [PATCH 071/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) { From 2d156a3c08ae8c76df6004e0a9c3a51aeadb21d0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 10 Oct 2023 22:10:28 -0400 Subject: [PATCH 072/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); From dbdd9b9d80bc14446d61f65dc2cef45aa0d2375a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 11 Oct 2023 00:39:18 -0400 Subject: [PATCH 073/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; From 134e258751f54bc547638022425d0a2c9e6013d3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 11 Oct 2023 00:42:38 -0400 Subject: [PATCH 074/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; From bd95b023c5744cb4fa85b4c35960de8b73492b3c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 11 Oct 2023 20:19:28 -0400 Subject: [PATCH 075/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) From 1d36be2537dad10f7e33d16ef8ebf5b83fb0e44d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 14 Jan 2024 12:33:59 -0500 Subject: [PATCH 076/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 8566afb20..eee17d206 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1072,7 +1072,7 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( Device.EnableDeviceVsync, 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(), LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", From 45f5d994367eadfdd1c59eab676192e3e2af59a1 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 14 Jan 2024 16:50:05 -0500 Subject: [PATCH 077/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) From d397e9477aa132b35b86841fe217a9a66bef9c53 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jan 2024 16:08:57 -0500 Subject: [PATCH 078/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) From 84d0b3b052d00172b423bc06ac14ba3c53c69dbb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jan 2024 16:09:16 -0500 Subject: [PATCH 079/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, From c3aba3a6d58bf93e4c983e4756583a7850d18c2f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jan 2024 16:09:24 -0500 Subject: [PATCH 080/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(); } } } From f959a910eb9842ea34b4e4a184f35c38f11272f0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 14:32:59 -0400 Subject: [PATCH 081/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 5682c48c1..7f25b7b79 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,7 +38,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 6718b7fcc..41d24a2fa 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -52,6 +52,7 @@ + @@ -59,6 +60,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 +} From 84d9ec13b91eae5c4bacafed45ff132e7bee655f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 14:48:54 -0400 Subject: [PATCH 082/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 eee17d206..a995e36bc 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -832,7 +832,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 d055d9ea4..f137dab99 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(); } From 6e2dfefd8fca4380fa2864a43c1b9d2b853566b3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 14:51:44 -0400 Subject: [PATCH 083/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 a995e36bc..8cc1d65a0 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -26,9 +26,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 41d24a2fa..a0815737d 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -52,7 +52,6 @@ - From c9f7d7e85a8ef4a4d4fb678257b545b870316ff6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 14:57:23 -0400 Subject: [PATCH 084/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() From a104af74102a19b9c2f51ff26ca4fc94d7d2b843 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 18 Mar 2024 15:03:10 -0400 Subject: [PATCH 085/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(); + } } } } From 7febe159fe3643de5ae702b619e46cf9668433f3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 14:05:09 -0400 Subject: [PATCH 086/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() From 4b80b015fbbb52c89a469dad59460a91c965ad49 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 15:07:35 -0400 Subject: [PATCH 087/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) From 67269fd9a9a67c8f04170969d9362a4fafcd52d0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 15:07:50 -0400 Subject: [PATCH 088/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(); } From 0de46a3a7075cd83550d1f98fbe9973254635dbd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 15:08:39 -0400 Subject: [PATCH 089/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); } } } From f7c9d77968e3dfb0654b57195ef909cc315a762a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 16:14:37 -0400 Subject: [PATCH 090/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) From 711fba5475b65bc945c88c4195395ff988166182 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 16:16:00 -0400 Subject: [PATCH 091/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) From 4560fe762bba101321d12b45fc0e30891e8c73ba Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 16:23:43 -0400 Subject: [PATCH 092/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; From a179c6111cb16a8694825e94b56be211dc80b028 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 17:18:59 -0400 Subject: [PATCH 093/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) { From 8fd4270012db0b5ed2bff83df485ce4e1921e70f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 19:02:05 -0400 Subject: [PATCH 094/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 + }; } } } From cc8bc3f921a1a0e7f9c05b5223f7b127645000d7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:04:31 -0400 Subject: [PATCH 095/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) { From 3f8c111eef6ad0bd05ee036f2578e99381f29247 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:11:48 -0400 Subject: [PATCH 096/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(); } } } From 54865122aa5b8780b8e496e529f681f0beb26ddf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:15:31 -0400 Subject: [PATCH 097/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; } } From 1b7634f2329abc21862b5bf69041ae858d948a45 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:29:14 -0400 Subject: [PATCH 098/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 From f611fc7103730db9f9210837e642fe31ceeca510 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 21:56:54 -0400 Subject: [PATCH 099/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); From 6d3f3b30b86f0b531f2dea1601638885af8d74bd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 22:14:17 -0400 Subject: [PATCH 100/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); + } } } From 0f41219e5fe4c1f124310b23ad538f294ca28179 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 22:58:27 -0400 Subject: [PATCH 101/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(); From 3e40dd08ecd487ef760f86a5857aacb877b2962d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 22:58:42 -0400 Subject: [PATCH 102/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; } } } From 65146bd3e4799ea498c4495f37a53828bc2f1f86 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 23:09:17 -0400 Subject: [PATCH 103/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; From 9afcbda1967ee8d81164f1432d3551493e625b06 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 19 Mar 2024 23:12:28 -0400 Subject: [PATCH 104/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) { From fa349408b1071aeff57fdf9dbc969983ea58cb31 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 14:35:19 -0400 Subject: [PATCH 105/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 7f25b7b79..cfb884d01 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,7 +38,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); } } From 1f7a5901aded4fca4e9de252f4fb04867e177f71 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 14:35:35 -0400 Subject: [PATCH 106/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(); } } } From db6788ec3a1fa0e91aad3418e497fe78341f7d63 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 14:35:47 -0400 Subject: [PATCH 107/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; + } + } +} From 0162925ff685e1ee011121ed18c392b42212940f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 16:37:08 -0400 Subject: [PATCH 108/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) From 9ce7c5550ce1f0dbd07f437782443f99347f0433 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 20 Mar 2024 18:16:11 -0400 Subject: [PATCH 109/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)})"; From 841e3bb3f91f3d6abdff0d3f897091af1dfcf0e9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 21 Mar 2024 11:35:01 -0400 Subject: [PATCH 110/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; } From 37b08ebaef14c733cb6a1bced12b453b7b896fbb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 21 Mar 2024 11:44:45 -0400 Subject: [PATCH 111/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; + } } } From aa53977e98d0f43f12a23c39ee1c6ed60bb5af8b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 21 Mar 2024 11:45:50 -0400 Subject: [PATCH 112/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++) From f56b826da94bccad22a7b7c4b249c2fb140a729c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 22 Apr 2024 17:44:55 -0400 Subject: [PATCH 113/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 | 5 ++--- 5 files changed, 33 insertions(+), 9 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 8cc1d65a0..f88bac0cd 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1054,6 +1054,7 @@ namespace Ryujinx.Ava { GraphicsBackend.Vulkan => "Vulkan", GraphicsBackend.OpenGl => "OpenGL", + GraphicsBackend.Metal => "Metal", _ => throw new NotImplementedException() }, $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); @@ -1072,12 +1073,10 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( Device.EnableDeviceVsync, LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", - ConfigurationState.Instance.Graphics.GraphicsBackend.Value.ToString(), 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}")); + $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %")); } public async Task ShowExitPrompt() From 451053c3d964a18bb1e2dc2a0cf4ebe7e99d2bb2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 22 Apr 2024 17:51:31 -0400 Subject: [PATCH 114/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(); From 9835682c7580fe1aab68c07dc01d4d63ed417a72 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 16:36:01 +0200 Subject: [PATCH 115/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() From 1956a60616cbeb4709e54591833d07dfc02dee99 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 17:01:11 +0200 Subject: [PATCH 116/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), }; } From 00009666a7dfa26a3c0eb2dd2c2449bf4e8daa24 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 17:41:16 +0200 Subject: [PATCH 117/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)})"; From f9e24fd08749f6aecc066fef5781faff15691dbc Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 17:57:42 +0200 Subject: [PATCH 118/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) From 74cee40e33ad5e5e41a32a7ccabfbbd4457d1ac9 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 20:51:53 +0200 Subject: [PATCH 119/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() From 0cda74a78011a756f6563b0e5e0be195dd900db3 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 14 May 2024 20:55:26 +0200 Subject: [PATCH 120/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, From 6e524358d4eb0b2ecb1c732f62d868049e10bf99 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 15 May 2024 09:03:53 -0400 Subject: [PATCH 121/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(); From d1a60c7053427c26abf83cc4742411b585d36ca6 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 15:49:29 +0200 Subject: [PATCH 122/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) From 299ca72b7a6e1d64dd443d0a96c6982c351baee6 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 15:55:03 +0200 Subject: [PATCH 123/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; From e4534bb0e00e83a73496e0f10ab8246946aac823 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:01:57 +0200 Subject: [PATCH 124/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); From 05eedc5066713f47f3fbc3e46ba0e2bc18355afa Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:06:35 +0200 Subject: [PATCH 125/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) From 5741fb90f9283913556389aec4ceed8b050471e2 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:20:52 +0200 Subject: [PATCH 126/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 + ""; + } } } From d13e4c5101d9ac80a55fe198169470fe58b393ac Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:24:13 +0200 Subject: [PATCH 127/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(); + } } } From 4a65260655017a9e00376facc916823cad745cca Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:33:46 +0200 Subject: [PATCH 128/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; From 200da3dc55c2847e1d16881c18c449090be07b83 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:42:19 +0200 Subject: [PATCH 129/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"; } } From 97f8c836d65c3b46eab31a216d6659c91a76fc05 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 16 May 2024 16:54:54 +0200 Subject: [PATCH 130/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) From 6d3df3a4ab017106549e17639df9287d9a53c9ae Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 16 May 2024 15:59:56 -0400 Subject: [PATCH 131/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) }; } From a568e19434f967b037da537a7e10dff32d88126e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 16 May 2024 20:29:37 -0400 Subject: [PATCH 132/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(); } From 271d185861a49b06235a3494ca30a30153d92705 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 16 May 2024 20:54:27 -0400 Subject: [PATCH 133/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) From 369476f775b2c9a7c912aad4067d38d0034a253e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 16 May 2024 21:06:04 -0400 Subject: [PATCH 134/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 { From 741b74a9915b0ed218ac769bab752636c0a9d5f4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 18:54:55 -0400 Subject: [PATCH 135/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; From 1433218fad9c5b821b435c534391cf0e74444dac Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 19:59:38 -0400 Subject: [PATCH 136/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; + } + } } } From 07e329a37c5f928a0d191e4f570c9e64bc32bdc2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 20:27:27 -0400 Subject: [PATCH 137/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(); From 0a7cec1a29d2ab023cfbf93920b599f87cc43cfd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 20:40:37 -0400 Subject: [PATCH 138/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; From 500aff53b187d920586802f1a2eae0fd50ce04c6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 20:47:45 -0400 Subject: [PATCH 139/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); } } } From c70f7005dd03844fbc11a0f875c187cdee0f7659 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 21:02:49 -0400 Subject: [PATCH 140/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) From 12fc642fb61ab671d580da78e8396b27aea34484 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 21:07:05 -0400 Subject: [PATCH 141/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() From f72a5ccdf9e7d21a6ac7d2fa32661ab1776ef489 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 21:20:15 -0400 Subject: [PATCH 142/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) From 205df929428339b0df0985dba8c9350b66daf143 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 21:29:46 -0400 Subject: [PATCH 143/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) From 408b72bce17f16c879b43337a11590d35093662c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 22:06:53 -0400 Subject: [PATCH 144/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) From 503291e5d957aeb025e7a9afc3088917052352da Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 22:17:18 -0400 Subject: [PATCH 145/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; From ac96cb75871d0f370f75428108e72f044737626c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 22:27:18 -0400 Subject: [PATCH 146/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; From 9e8b333d9c14727b3725a56d6d71ae7fa1b5656b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 18 May 2024 22:29:03 -0400 Subject: [PATCH 147/368] Remove rebase garbage --- Directory.Packages.props.orig | 61 -- src/Ryujinx/AppHost.cs.orig | 1226 --------------------------------- 2 files changed, 1287 deletions(-) delete mode 100644 Directory.Packages.props.orig delete mode 100644 src/Ryujinx/AppHost.cs.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) - - - - - - - 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; - } - } -} From c3d1f93bc16fcb060fc03167e2db708645f11f9a Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 08:08:12 +0200 Subject: [PATCH 148/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() From 77ca515e06ad0473becbecb2c8fc031f366334ed Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 08:09:06 +0200 Subject: [PATCH 149/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); From 5c34de072c5a104845d9b26eddbe5731411698e2 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 09:10:14 +0200 Subject: [PATCH 150/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) { From 45f7e4ce53e249c55a0ffeaf77417be2e26ed199 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 13:25:52 +0200 Subject: [PATCH 151/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); From 4235eddfd2de842f6773a5e9e99505549a4f4432 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 13:32:09 +0200 Subject: [PATCH 152/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) From f3885d72e53467b28f82cecde7cb03ca97c46b37 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 16:47:43 +0200 Subject: [PATCH 153/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) From e2fc86a67e2b79113e72d6365565bc61ed1b75bb Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 16:51:05 +0200 Subject: [PATCH 154/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); From a1851c480ebf930d99d70f07fa73c803a872fbbf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 19 May 2024 10:32:24 -0400 Subject: [PATCH 155/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) }; } From d72e63978ee759baa064a065774cb5df65da522e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 19 May 2024 10:44:08 -0400 Subject: [PATCH 156/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) }; From 19f53084fc0bd52702407ffd55d0a86f7ac91aea Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 19 May 2024 17:02:10 +0200 Subject: [PATCH 157/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; From 9e799f55898dc5cb9318828ebcb6b1f9c30b59bb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 19 May 2024 11:05:50 -0400 Subject: [PATCH 158/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); } } From 4d24cc6e634195814a1edc73864cc66e0247ae06 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 19 May 2024 11:07:55 -0400 Subject: [PATCH 159/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); From d77f5a7fb1c8ebc20a13dc307532ad316a812124 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 17:28:00 +0200 Subject: [PATCH 160/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; + } + } +} From bde83d846952408db12474eda7e191e354f22df0 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 17:31:24 +0200 Subject: [PATCH 161/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; - } - } } From c3575ce1157ac399e93680df0f4a434b92b1e277 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 18:38:08 +0200 Subject: [PATCH 162/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), From 89c29152d0630b44a21c433e199c40e97ca662db Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 19:07:27 +0200 Subject: [PATCH 163/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) From 0d7fb3aaef385d0353166b55c0ca27ce719d9b5d Mon Sep 17 00:00:00 2001 From: Samuliak Date: Mon, 20 May 2024 19:12:17 +0200 Subject: [PATCH 164/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; From 0b2984e4b6321c2916fa15fedb6c4ed70930f3db Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 21 May 2024 16:23:42 +0200 Subject: [PATCH 165/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; } From 290b8657d24c9b7e787a1feaf68f00954e91d87c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 15:02:21 -0400 Subject: [PATCH 166/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) From 3044457dcab23bd2399e1ee6fc02125089b540d4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 15:04:44 -0400 Subject: [PATCH 167/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), }; From 8e9f7f48299c62ae48c6cf511f551c01a56946f5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 15:07:12 -0400 Subject: [PATCH 168/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 From 1984c5af7e62ceba01ebe657aa286447d249a0ff Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 15:44:00 -0400 Subject: [PATCH 169/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) { From 52978986c7cdb85aea04c491bd3b70bbd5d655ad Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 20:27:37 -0400 Subject: [PATCH 170/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), From d79f6b9743f76295a294e4ed3513d20cb8e85e0d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 17:21:44 -0400 Subject: [PATCH 171/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; +} From f8362587286065030139e45f738f38b560cc28e2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 20:26:54 -0400 Subject: [PATCH 172/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; } From d5f510a745974bdc4e3ff4505d8ca6fe75b5850a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 21:32:24 -0400 Subject: [PATCH 173/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 }; From 57d68ce7f02f9078723ddc8d588fb06bc28aeccd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 22 May 2024 22:01:39 -0400 Subject: [PATCH 174/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", From 94b253476e9ff655ae5f07be3639382ecbb03717 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 23 May 2024 15:48:20 +0200 Subject: [PATCH 175/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; From 5bf7e146fa79083be0de2cdbb8ce25b7aaf37e95 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 23 May 2024 17:49:22 +0200 Subject: [PATCH 176/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() From 8ad4ee28924e2ad951fa6ec0bdaf891bb7ba360f Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 23 May 2024 18:23:01 +0200 Subject: [PATCH 177/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) From 67e83af94393dacfc27845b22ab13287064e3f6e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 23 May 2024 13:15:23 -0400 Subject: [PATCH 178/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 cfb884d01..70404ef8e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,7 +38,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(); } } } From a42b70890bbda7ac76e13e033b6bad29a8c3b46b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 23 May 2024 14:08:34 -0400 Subject: [PATCH 179/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 = "."; From 854c25e0a63cbeb15989a5fa2cee885d19db2414 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 23 May 2024 14:47:05 -0400 Subject: [PATCH 180/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, From f93d309bff8bf61f4b71024738da694dffa3a9fd Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:32:02 +0200 Subject: [PATCH 181/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 70404ef8e..afef0d7ff 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,7 +38,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() From 19c5391e7dffc1a25e72674f11e6d725c467d090 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 14:16:42 +0200 Subject: [PATCH 182/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; From 56fcfba68954b3c4aec9c7cc78b51db3a57af55a Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 14:41:51 +0200 Subject: [PATCH 183/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() From 37272c62238865cb2d7a40f8fc1353543eaa2a1b Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:09:06 +0200 Subject: [PATCH 184/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); } From bff52c221bc3809b43402caa3f7276c488f86bb7 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:28:16 +0200 Subject: [PATCH 185/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}; } From 6296de1a65eb281f09e5c6d7479647765a5fe113 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:52:17 +0200 Subject: [PATCH 186/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 afef0d7ff..70404ef8e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,11 +38,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() From 541f1096b7edb4d1d89a5833124e47d916ed2b76 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 15:59:07 +0200 Subject: [PATCH 187/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; From c3c75b29392fdf5063bda981e6033b6ebe3c6160 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 16:40:01 +0200 Subject: [PATCH 188/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"); From b051cda16a5c708371f241a2b8adf8a63c6721b8 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 17:59:49 +0200 Subject: [PATCH 189/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() From cb183b39455128ac42c5ff31bc2117c83b99460a Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 18:37:31 +0200 Subject: [PATCH 190/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) From 4a97c2b64de69b7e072c2827dd8e0125df2d3ea8 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 18:41:36 +0200 Subject: [PATCH 191/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"); } } From 952ed6bb412e6af49859b7817964d55403563387 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 24 May 2024 20:03:55 +0200 Subject: [PATCH 192/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); From af4b762ce34b91c058d32d2d4e3be84caa00a48b Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 08:17:43 +0200 Subject: [PATCH 193/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 From 680f950a599761450f1a111b957bf023fc05c943 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 08:27:28 +0200 Subject: [PATCH 194/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(); } } } From 934a5941b97e387666f67bfbbc9926c78517755d Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 08:39:45 +0200 Subject: [PATCH 195/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); From 6d12cb529d38d850e51817cc2ca6ca145053327a Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 09:51:56 +0200 Subject: [PATCH 196/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)); From 24158b2e1cd1b6f1fef515d8d218bde18a9babcc Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 09:56:42 +0200 Subject: [PATCH 197/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 From 09983b818838110f5f3790dcce72aabc208725f7 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 13:15:30 +0200 Subject: [PATCH 198/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 From c8d51c693e7b7405f7c9853aba8e455869647308 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 11:03:45 +0200 Subject: [PATCH 199/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 From 330935e780d57e4599a7f1480ac21e22a2d4c77b Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 14:38:39 +0200 Subject: [PATCH 200/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() From de01995e95dc702fe6b07d870f11d1ff7204a2e2 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 14:48:07 +0200 Subject: [PATCH 201/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()); } } From f7fc475ee4ab5328bc785ff56704e266110531d9 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 25 May 2024 15:23:13 +0200 Subject: [PATCH 202/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(); } } From c19dee2f8ec4dccf65e5f23beab62b88d7b35880 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 25 May 2024 12:12:58 -0400 Subject: [PATCH 203/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"); } From a14ca383b21498f8670461c87f591c715abcf917 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 25 May 2024 12:21:42 -0400 Subject: [PATCH 204/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(); From f837be962fac0c1575b17c9243817c400e82aa9c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 25 May 2024 12:30:06 -0400 Subject: [PATCH 205/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) From 5f6d375b00d1f02af052753cd7a3b741aa893650 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 206/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) From 8cbdbad67ff62a038c0d6d83364bebc4b90e453e 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 207/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), From b7915ac3cfd15a674ae584319ec10ef3f64624f2 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 208/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() { From eac8ac23ad0a662a03c229cbeabc0f415a20ffb0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 10:34:16 -0400 Subject: [PATCH 209/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(); } From 4f649f5650f5a6c0fbc62355eac997da3b4631d0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 10:38:00 -0400 Subject: [PATCH 210/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) From 2225aa17e332fab8ee68a3b3ebcdb61c68166f0e 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 211/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) { From 9238b6723c09ad62a25b3e2ed747f22cc8f32772 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 18:09:29 -0400 Subject: [PATCH 212/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; } From d8f9aaeaacbc65fd50ac2abca1c1b24e3aab846a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 21:35:32 -0400 Subject: [PATCH 213/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) From 01122ca032b8cde58dd50c1ad4aeb6d6d8268f61 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 21:46:43 -0400 Subject: [PATCH 214/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; - } - } } } From 2941f02c8784364cb141256946227c6de2ec03f3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 22:00:48 -0400 Subject: [PATCH 215/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++) From 63ab5b063976265a117388ee542d9451a65c4221 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 22:36:59 -0400 Subject: [PATCH 216/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)) }; From 4e130e7e38bdad74cdcb97a5fc1cf64d8d79585f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 27 May 2024 23:41:11 -0400 Subject: [PATCH 217/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) : ""); From de2a8cece495291f2e3f7b8b91691b7cb9b591dc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 28 May 2024 01:45:59 -0400 Subject: [PATCH 218/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; From cc747047cd14b70de1de649fd87046801f77d664 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 28 May 2024 02:18:59 -0400 Subject: [PATCH 219/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; } } From 3ff27f541ede19f9779cbbc11f090423e072e52f 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 220/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 27aacac15..f3599cf67 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 86fab760f..49cfce137 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) { From 638b9e5f69a67adf211502ddd8cbe67593601ef1 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 16:24:49 +0100 Subject: [PATCH 221/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; From 2996f96a4244b9ba339bc771fd2ac078415368f3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 16:31:49 +0100 Subject: [PATCH 222/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; From 8a6fde9fead7cbb78d809bf971ee262b78dd17d6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 16:38:11 +0100 Subject: [PATCH 223/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_"; From 159db87d86f5120183475bc4212777b547e45ffb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 17:14:01 +0100 Subject: [PATCH 224/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 From 5b47001b3868d1cab3e88a37b792656117006320 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 18:31:10 +0100 Subject: [PATCH 225/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), From d694878d2ce083edf5474897b835e3034253aa38 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 23:10:00 +0100 Subject: [PATCH 226/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; } } From 5322de91b566b53a6e9e055d4bb1b95a4e9ae089 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 29 May 2024 23:52:29 +0100 Subject: [PATCH 227/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); From fdfd457d6e8aa48fbdcda7f3c88148f06c08ed88 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 02:14:56 +0100 Subject: [PATCH 228/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)}"; + } } } From 66f68e08a3b2b5b3edb0fa42d6297de95a1a9e59 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 02:23:37 +0100 Subject: [PATCH 229/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 From fe7b8c45141c5455280ac2a3a69861d214af07d3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 13:20:16 +0100 Subject: [PATCH 230/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() From 0c351a6caa0e2d17c6ec7560dc75cd1134f59b7a Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 13:20:37 +0100 Subject: [PATCH 231/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); + } } } From 8e2e6c48de59053f720f608337a82dc398f732cf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 13:24:05 +0100 Subject: [PATCH 232/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); } From 4a4ac78373eea1d2453b077299c5afe5a3ca527d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 16:11:48 +0100 Subject: [PATCH 233/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) From a2328bd8fb4b8daec55b55ae6e7e74a788b75f69 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 21:26:20 +0100 Subject: [PATCH 234/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, From 1d96ca6c80976f03fdd16b89607a848d4b4545f4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 30 May 2024 21:46:16 +0100 Subject: [PATCH 235/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, From 87b46ad5c1a42bf57d2908cdd014b4dce6e5844c Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 31 May 2024 07:45:14 +0200 Subject: [PATCH 236/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 }; } From e6eb99789220f8a22e7c9f6b1e3822fc5b3a51af Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 11:24:33 +0100 Subject: [PATCH 237/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); From b0f167a13be2618ee58b0e61c707beba42f52406 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 11:43:26 +0100 Subject: [PATCH 238/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) From 04703f019100e1bd038634bd3fc1ba6f0cad0352 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 13:20:39 +0100 Subject: [PATCH 239/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) From 774359f6b7b930287287c42ca3a41dd0bba4018d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 14:01:32 +0100 Subject: [PATCH 240/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); From dee19f822754c618d973213365617dae84d522ac Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 14:02:22 +0100 Subject: [PATCH 241/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; From 7158c8ddc2be8741c933acef4c433752249e229b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 31 May 2024 14:03:38 +0100 Subject: [PATCH 242/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; } } From e016da58e127715e9733d7b68434e3e6110e6a75 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 243/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(); } } } From 45239b3810676d5477d9e04638c3e4fd4934e751 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 19 Jun 2024 23:14:23 +0100 Subject: [PATCH 244/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); From 04982f792e7c986eee6664713d85dc8b43b7ff39 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 245/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); + } + } +} From 705ec71f657e558229d8946010db412db4bb1f84 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 14:14:05 +0100 Subject: [PATCH 246/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 From de4d4016b3115d6e0fcf961fb575b2c5f4bf1cfa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 14:25:40 +0100 Subject: [PATCH 247/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); From a197340e22fdd58b24ec579980acae4a64a0dd3b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 14:29:45 +0100 Subject: [PATCH 248/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); From 0561d708f876558322820b3515d2d3084f7e8b3e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 19:11:12 +0100 Subject: [PATCH 249/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); } } From be78e7a1a5745333b42d5393e9109066f812ce97 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 21:08:28 +0100 Subject: [PATCH 250/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); From c59142a5007f1e250f2b0f2694970bff3eba9cd9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 20 Jun 2024 22:55:33 +0100 Subject: [PATCH 251/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); From 36f7d4eafa50eacfba02ea9476a3316e3dd765d6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 00:15:14 +0100 Subject: [PATCH 252/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(); + } + } +} From 6550f1cd660f229ca745a2a4237167807bb99aaf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 00:21:06 +0100 Subject: [PATCH 253/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; From a83eb53abf6c3ead1a124598e71c39a388ffa1f8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 00:54:04 +0100 Subject: [PATCH 254/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(); From e4fa286698d132ce034f9409bbe6b2d189793814 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 10:31:21 +0100 Subject: [PATCH 255/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) From 461933b8cd6c283644141da3e380465090517c9b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 16:21:58 +0100 Subject: [PATCH 256/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); From 3fd758532b610bc8dc36796b76352f5cc7e2a31e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 16:32:31 +0100 Subject: [PATCH 257/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) From b54bfb1a7c3fee02108f615d6382ea7213427a02 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 16:39:27 +0100 Subject: [PATCH 258/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) From 3fe47449b4dcb63c18436acff5eb6338813ef3d5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Jun 2024 16:58:58 +0100 Subject: [PATCH 259/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) { From 23b9a780eed9103511adbe555cfd0da8e87077fa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 22 Jun 2024 14:38:09 +0100 Subject: [PATCH 260/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 e0a92da5a..7803d9aa5 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; From e6f4745bb5b0ae6435f6383957045e7b20325438 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 261/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) From 4dad6d12b99e89c396e7268903a3f16eecb315d1 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 25 Jun 2024 14:51:54 +0100 Subject: [PATCH 262/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), From f16693e4e133cea6fa3ff97541ff317be92a00f9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 12:39:05 +0100 Subject: [PATCH 263/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) From 8a006514d3618b3e6c02dc5622c31d5bc1833695 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 12:39:25 +0100 Subject: [PATCH 264/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); + } } } } From 97c22e2f1a62ef1c44bb48794998735e4a24f531 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 15:00:22 +0100 Subject: [PATCH 265/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) From ea957130569970db5277fbb49478664d8656ed60 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 17:38:23 +0100 Subject: [PATCH 266/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; } From ba21266a3ed58633af2c7cb6413675984918e4c9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 18:43:16 +0100 Subject: [PATCH 267/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; } From e2c0b1b1ec0779e505ddeb4e36eb71c8a4c9cd4b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 22:26:27 +0100 Subject: [PATCH 268/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; From 17a27cf59b16129dcbb7a8bbfa268f0bc7574d8e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 22:34:21 +0100 Subject: [PATCH 269/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 From 55a5b1e75eeb86299545bfec0f69dddec4e52329 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 22:38:43 +0100 Subject: [PATCH 270/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; From f61f587d37ce0bf5f0a7e2b529e2bd1b68a569cf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 26 Jun 2024 23:52:38 +0100 Subject: [PATCH 271/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) From cb65fdf95a491e4afd40b4a73132be04211baffb Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 27 Jun 2024 00:13:37 +0100 Subject: [PATCH 272/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 From 5f1d3fd7448b0357e2d54782494ed9585e4357c1 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 27 Jun 2024 00:20:00 +0100 Subject: [PATCH 273/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; From b950e12ab89c64696d785354d60cabde2dcaf71e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 27 Jun 2024 22:51:12 +0100 Subject: [PATCH 274/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() From 88ce186191e8569266918432b5d4033ba5069956 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 28 Jun 2024 21:13:59 +0100 Subject: [PATCH 275/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(";"); From ac5932cd0272c5fb6a0666577fbabead88acf463 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 28 Jun 2024 21:14:53 +0100 Subject: [PATCH 276/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) { From a33187f7db59968164423c57219659c2defa0a4f Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 29 Jun 2024 19:07:07 +0100 Subject: [PATCH 277/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 e1e696ca8..b34aa8fe9 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 f3599cf67..27aacac15 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 49cfce137..ada1f3e97 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) { From f3feabad2e69316c8edef40583546302f69740f6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 29 Jun 2024 22:54:28 +0100 Subject: [PATCH 278/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) From 459d1d6e54b4b0686c73ad14dbe1bafe1ee5fa74 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 30 Jun 2024 12:01:24 +0100 Subject: [PATCH 279/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) From e3dd174f28cd3e49502f9eec6a4d7cde4ee22026 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 30 Jun 2024 17:23:53 +0100 Subject: [PATCH 280/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) From 117c5c1645d44d0267d9a723f9fd12e5b7c2dba2 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 30 Jun 2024 19:04:28 +0100 Subject: [PATCH 281/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; From f59fae3612eb66dc81a7bd3f74470871413998c3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 1 Jul 2024 18:01:53 +0100 Subject: [PATCH 282/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) From 2507d351af083650c6cf890c8f8ea58589058adc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 1 Jul 2024 18:02:43 +0100 Subject: [PATCH 283/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 { From 18a3ee16bb326fbd8362b2bf2a6c71a60df81153 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 284/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(";"); From 4909b73332758b25bea59f3eab652e7abbef338b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 1 Jul 2024 23:07:02 +0100 Subject: [PATCH 285/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++; From 9a0d50817cdcfdb7f30d136a3f6a4029358ecfb2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 1 Jul 2024 23:36:11 +0100 Subject: [PATCH 286/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( From 19b089a236666739dc2d44513ee1618e37517d87 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 2 Jul 2024 12:00:37 +0100 Subject: [PATCH 287/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; } } From bf1884c259a3a65d148ad9800bf2fa23a32961e0 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 2 Jul 2024 13:41:41 +0100 Subject: [PATCH 288/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; From d111b152d1ba580bf0be1c0dbd2fb59d85614805 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 2 Jul 2024 17:49:19 +0100 Subject: [PATCH 289/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) From c92a9e0ae57ac78e075d4ec6e382b736ee23256e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 2 Jul 2024 17:52:53 +0100 Subject: [PATCH 290/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, From 4b95934fefec73f254a7a219bebf0e916ce1ec53 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 3 Jul 2024 10:27:03 +0100 Subject: [PATCH 291/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); + } + } + } + } +} From 01094222c77c5ca5c934d3b4786a7625dc37948d Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 3 Jul 2024 13:43:45 +0100 Subject: [PATCH 292/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(); + } } } From e1568613af865f1ebb08575cdfb637f2cbef301a Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 3 Jul 2024 13:46:54 +0100 Subject: [PATCH 293/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( From 5a8e070c04877f3f61ef63ca09f261fdb9ffe317 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 3 Jul 2024 23:58:27 +0100 Subject: [PATCH 294/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")] From 16a9b18c26041f570c19bd49495b44785e65bf34 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 4 Jul 2024 10:19:40 +0100 Subject: [PATCH 295/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), From 6f440f53f47cb8cd2ed07ce8438a50043111e614 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 4 Jul 2024 19:34:14 +0100 Subject: [PATCH 296/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); From 8369f15bf5ea99c26cb441a4899cdaa959a8fe55 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 5 Jul 2024 09:35:17 +0100 Subject: [PATCH 297/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) From 1a56e4477a2c849112c19a22e9d7f2398b6b80fa Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 6 Jul 2024 22:33:49 +0100 Subject: [PATCH 298/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) From f8e232404ed6139b520cabf940ba83135fb7c780 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 6 Jul 2024 22:38:02 +0100 Subject: [PATCH 299/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 ada1f3e97..86fab760f 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; From fa9d38c77999ef9ae8b02e1f85619d7ccb10d3c5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 6 Jul 2024 22:56:04 +0100 Subject: [PATCH 300/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(); } From 10c042b6ab7c77f965282b7866177314324c815d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 8 Jul 2024 12:02:42 +0100 Subject: [PATCH 301/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) From 1b2097c6ebbac50fd2ef7d22e264fac516a3d058 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 8 Jul 2024 13:18:48 +0100 Subject: [PATCH 302/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) From 02b9b9272349d21c5f0bf3a808c0b1d038ef5471 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 8 Jul 2024 13:55:46 +0100 Subject: [PATCH 303/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); + } + } +} From a8794b2d58a11e1213f61a39756ab758dd7203a9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 19 Jul 2024 19:39:58 +0100 Subject: [PATCH 304/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; } From a32b619f5c19fe1dc740d0e2905345e7b2357d8b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 19 Jul 2024 19:45:09 +0100 Subject: [PATCH 305/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); From 32a3d1df4d17f38e984322e11b94801d78de0902 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 22 Jul 2024 13:40:10 +0100 Subject: [PATCH 306/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 70404ef8e..47cce9599 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,7 +38,7 @@ - + diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index b34aa8fe9..d0bebf62f 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) From 1500af5808a73a464a638ba9e8d156bd24768715 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 12:13:40 +0100 Subject: [PATCH 307/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} "; } } } From fdf5d49985b91ce50d1ac98138a679b4fa3267ce Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 14:58:56 +0100 Subject: [PATCH 308/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) From 61c7d3bb9c792f573ca48257b4cd56bd83e096f2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 15:23:16 +0100 Subject: [PATCH 309/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)}"; From b4cf2d1cc942e85fc8fc2e35f97b77f7b22ae018 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 15:35:50 +0100 Subject: [PATCH 310/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)}"; From 1bb3f28504e82cc5962a78b39ba2fdcda0f41cb7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 15:43:53 +0100 Subject: [PATCH 311/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 7803d9aa5..ad26cbd56 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -19,5 +19,6 @@ + From 6be0890f3435539701519306c8a364ffb299d372 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 16:25:03 +0100 Subject: [PATCH 312/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, From bdcc2fd90e020d76cbdc641fd86ddc16105cafbf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 21:02:19 +0100 Subject: [PATCH 313/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; } From 10110198f770133cdfd4f9b2794615be2e454a21 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 21:53:17 +0100 Subject: [PATCH 314/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) From f07d957bda7879554ee0b881d2b23282b99c5887 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 24 Jul 2024 23:27:59 +0100 Subject: [PATCH 315/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) From c6bfc660ef3448e4da7a98e37d720c5a90de3dee Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 25 Jul 2024 12:22:08 +0100 Subject: [PATCH 316/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"; From 04499647602ed93e6ee11fb9e754f44c9d202eed Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 25 Jul 2024 18:58:26 +0100 Subject: [PATCH 317/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) From 27e9671da431911f2c7fce5ed2c047e75a6fe6ad Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 26 Jul 2024 13:58:25 +0100 Subject: [PATCH 318/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(); From e8e18f257853dac51fc17cae9e9b19cebb735143 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 26 Jul 2024 17:12:31 +0100 Subject: [PATCH 319/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++) { From b3e002f8dc2ba247b5cddbe283b2433746c77659 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jul 2024 11:31:29 +0100 Subject: [PATCH 320/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][]; From 1987309fda7655ba79a3aa996579b447335bd0c5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 27 Jul 2024 11:32:45 +0100 Subject: [PATCH 321/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 { From a72ce9998f4a09df9b6c5853be2bffb06a68f16c Mon Sep 17 00:00:00 2001 From: Gabriel A Date: Sat, 27 Jul 2024 17:01:08 -0300 Subject: [PATCH 322/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(); From 7de9bffe911bca203079ea169a26c73c33cc3732 Mon Sep 17 00:00:00 2001 From: Gabriel A Date: Sat, 27 Jul 2024 21:04:56 -0300 Subject: [PATCH 323/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 { From 479ea9f8e8e6f72f3078ea59e4fef750448d78d5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 28 Jul 2024 18:45:43 +0100 Subject: [PATCH 324/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) From 3429ee2318f6b6409ef7a1cd9df7383551ac7dd2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 28 Jul 2024 18:46:58 +0100 Subject: [PATCH 325/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() From 4ddecbb6fe76361cddc3ec859e18bb5aa9897522 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 28 Jul 2024 18:48:17 +0100 Subject: [PATCH 326/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. /// From 2fdd3522553d6ae74494468993f3dfc853ac7b55 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 28 Jul 2024 22:52:31 +0100 Subject: [PATCH 327/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); From 13afe36f633dd36d6957951263c3a2e7d21f1817 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 29 Jul 2024 00:51:53 +0100 Subject: [PATCH 328/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 { From c1745a27ab5bfce71350c4341df8c9f04eaef650 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 29 Jul 2024 01:02:59 +0100 Subject: [PATCH 329/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); From 4eb74a310b36cfa24e561c025ecbea433b5085e5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 29 Jul 2024 01:25:27 +0100 Subject: [PATCH 330/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)})"; } } } From ce2124437d6f2ee774a5fcac9e5a34bbd73b0adc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 31 Jul 2024 16:29:48 +0100 Subject: [PATCH 331/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() { } + } +} From fa1f91f96cc18abe984335a183b8d9030f7e03ad Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 31 Jul 2024 17:09:24 +0100 Subject: [PATCH 332/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) From 3336c86fe5d7e81dc7401ab4b493aadfd04da89a 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 333/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}"; } } } From 95318b9abee401d9b00aca2db2c3db426f6a7fda Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 00:37:37 +0100 Subject: [PATCH 334/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]], From c30c609624461abe136f0cbbbe29720c3c2d9209 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 11:52:14 +0100 Subject: [PATCH 335/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); } } From 5dc244ae68eca337ac77bf89be522ab2b4028d53 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 14:23:56 +0100 Subject: [PATCH 336/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" : "")}>"; } } } From 4848335975cb1fff58542acb224a92777907a61d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 14:39:11 +0100 Subject: [PATCH 337/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(); From 3e474d84e892a3ec865b3998e3593e3eb8b98391 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 15:51:06 +0100 Subject: [PATCH 338/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 ad26cbd56..6ba6d4225 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 From 4fed5634c21a1c514fa0d2160d932d1d1ab943dc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 18:13:49 +0100 Subject: [PATCH 339/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(); From 8674ea675209ca16afaffcd253bc189d7aadd984 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 1 Aug 2024 18:40:34 +0100 Subject: [PATCH 340/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(); From 3876f60c7f8daa8e57e96db6f91a18a0154084bf Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 3 Aug 2024 17:08:50 +0100 Subject: [PATCH 341/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; From 9e05ee9a4dbb74b53bae14cb9e2737ff4bccde13 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 5 Aug 2024 13:18:00 +0100 Subject: [PATCH 342/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, From 03c475d4b50eeba731378b24f6d4274dd5715acb Mon Sep 17 00:00:00 2001 From: riperiperi Date: Mon, 5 Aug 2024 22:58:37 +0100 Subject: [PATCH 343/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; } } } From ba63762ff597dcbf56c60a63e5f052f8f1a06396 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 344/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) From 8766378ce8f68acbfbcffe97ea4a29e9aebcf520 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 10 Aug 2024 21:07:20 +0100 Subject: [PATCH 345/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(); From cee1a8ee60796ed37e53284ba915ba15e34843de Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 11 Aug 2024 20:29:08 +0100 Subject: [PATCH 346/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) From a0ac4921ad0ff337ad33eafe232fa9c4fa541c1c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 12 Aug 2024 13:36:56 +0100 Subject: [PATCH 347/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(); + } } } From 29f62622604dcf5d9f6c643f8b7dc4201b6446ce Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 12 Aug 2024 14:09:40 +0100 Subject: [PATCH 348/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)); From 81c6b2923260240684622af20b28c1fdd18319fd Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 12 Aug 2024 15:54:41 +0100 Subject: [PATCH 349/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 47cce9599..e59d2d4fb 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,7 +38,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(); From 3d30f0f907dedf44c0c6977bcad14375f267648b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 12 Aug 2024 23:22:15 +0100 Subject: [PATCH 350/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; From 363c4a80ff402a81773b2270e43e3b4bdd61fc73 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 13 Aug 2024 17:03:19 +0100 Subject: [PATCH 351/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(); From 3bd4dc7f2b56b0a5cad5cafc7fe3eca2e803beb6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 13 Aug 2024 23:18:57 +0100 Subject: [PATCH 352/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)}"); From 4439e7dcdc38e7e86bccd5f9a81d76c27a25a315 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 2 Sep 2024 13:30:48 +0200 Subject: [PATCH 353/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++; } } From d30d23bd0f95623068378ae442ef65166444ba4d 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 354/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); + } + } +} From 6e332be51cfc3c306e7c0b5549fcc411f208c269 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 3 Sep 2024 20:53:31 +0200 Subject: [PATCH 355/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 545a0efcf..715d58dda 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -17,10 +17,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; From 76d7bef031804cd12c99c4f12c8855054e160343 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Sep 2024 13:18:48 +0200 Subject: [PATCH 356/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) From 252fd678fc74735eac809c1228d6c3d41718c184 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Sep 2024 14:08:38 +0200 Subject: [PATCH 357/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); From f5cb7e0f67bcdd02388324b0e4df4d9f782b187d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Sep 2024 20:18:38 +0200 Subject: [PATCH 358/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) From c14d8cf2eeeed7b4e1a48615a85b5f806580fcf2 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Sep 2024 20:42:53 +0200 Subject: [PATCH 359/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; From 94579f204238ebd91da87d7e590481a53cd0608e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 6 Sep 2024 13:33:35 +0200 Subject: [PATCH 360/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); From 34eddbcf6af604e497d169d9b1a7eabffe806fa9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 6 Sep 2024 20:00:12 +0200 Subject: [PATCH 361/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), From 26f6e770083a15aa9b3cc9d6bb4b2cb07abd6a3d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 6 Sep 2024 23:42:59 +0200 Subject: [PATCH 362/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(); From c62e00d90fde26be6bd386f2774ccf572451dff4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 7 Sep 2024 12:24:40 +0200 Subject: [PATCH 363/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 { From e81329c7b088ac0ef58ab2b2916dd166ca9818dc Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 7 Sep 2024 12:35:37 +0200 Subject: [PATCH 364/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) From 53cd0c0e7a233ae47dd48dad913a04d08dd310b8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 7 Sep 2024 21:38:01 +0200 Subject: [PATCH 365/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) From b0628d3d50defa34fc6e99a06b6301e5dba124e9 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sat, 7 Sep 2024 21:46:34 +0200 Subject: [PATCH 366/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 { From 97bc91af5233fe0fad810010384e3b196604b7ea Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 8 Sep 2024 01:29:36 +0200 Subject: [PATCH 367/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) From 79075e8386b6b442baa6622c67e8c8a26008f011 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Wed, 18 Sep 2024 23:52:24 +0200 Subject: [PATCH 368/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) {