diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 2482d3e9f..8ae980576 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -1248,7 +1248,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.ViewportTransformEnable == 0, GetDepthMode() == DepthMode.MinusOneToOne, _state.State.VertexProgramPointSize, - _state.State.PointSize); + _state.State.PointSize, + _state.State.AlphaTestEnable, + _state.State.AlphaTestFunc, + _state.State.AlphaTestRef); } private DepthMode GetDepthMode() diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs index 73c35ae50..bd7a86da8 100644 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs +++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs @@ -170,7 +170,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache false, false, false, - 1f); + 1f, + false, + CompareOp.Always, + 0f); TransformFeedbackDescriptor[] tfdNew = null; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 4f8af41df..ebbd18b5c 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -1,6 +1,8 @@ using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; using System; using System.Runtime.InteropServices; @@ -16,6 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private readonly AttributeType[] _attributeTypes; private readonly int _stageIndex; private readonly bool _compute; + private readonly bool _isVulkan; /// /// Creates a new instance of the GPU state accessor for graphics shader translation. @@ -32,6 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Shader AttributeType[] attributeTypes, int stageIndex) : base(context, state.ResourceCounts, stageIndex) { + _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; _channel = channel; _state = state; _attributeTypes = attributeTypes; @@ -74,6 +78,33 @@ namespace Ryujinx.Graphics.Gpu.Shader return MemoryMarshal.Cast(_channel.MemoryManager.GetSpan(address, size)); } + /// + public AlphaTestOp QueryAlphaTestCompare() + { + if (!_isVulkan || !_state.GraphicsState.AlphaTestEnable) + { + return AlphaTestOp.Always; + } + + return _state.GraphicsState.AlphaTestCompare switch + { + CompareOp.Never or CompareOp.NeverGl => AlphaTestOp.Never, + CompareOp.Less or CompareOp.LessGl => AlphaTestOp.Less, + CompareOp.Equal or CompareOp.EqualGl => AlphaTestOp.Equal, + CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => AlphaTestOp.LessOrEqual, + CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater, + CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual, + CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual, + _ => AlphaTestOp.Always + }; + } + + /// + public float QueryAlphaTestReference() + { + return _state.GraphicsState.AlphaTestReference; + } + /// public AttributeType QueryAttributeType(int location) { diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index fba67851d..486d93b5b 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -45,6 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public readonly float PointSize; + /// + /// Indicates whenever alpha test is enabled. + /// + public readonly bool AlphaTestEnable; + + /// + /// When alpha test is enabled, indicates the comparison that decides if the fragment is discarded. + /// + public readonly CompareOp AlphaTestCompare; + + /// + /// When alpha test is enabled, indicates the value to compare with the fragment output alpha. + /// + public readonly float AlphaTestReference; + /// /// Creates a new GPU graphics state. /// @@ -55,6 +70,9 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Depth mode zero to one or minus one to one /// Indicates if the point size is set on the shader or is fixed /// Point size if not set from shader + /// Indicates whenever alpha test is enabled + /// When alpha test is enabled, indicates the comparison that decides if the fragment is discarded + /// When alpha test is enabled, indicates the value to compare with the fragment output alpha public GpuChannelGraphicsState( bool earlyZForce, PrimitiveTopology topology, @@ -62,7 +80,10 @@ namespace Ryujinx.Graphics.Gpu.Shader bool viewportTransformDisable, bool depthMode, bool programPointSizeEnable, - float pointSize) + float pointSize, + bool alphaTestEnable, + CompareOp alphaTestCompare, + float alphaTestReference) { EarlyZForce = earlyZForce; Topology = topology; @@ -71,6 +92,9 @@ namespace Ryujinx.Graphics.Gpu.Shader DepthMode = depthMode; ProgramPointSizeEnable = programPointSizeEnable; PointSize = pointSize; + AlphaTestEnable = alphaTestEnable; + AlphaTestCompare = alphaTestCompare; + AlphaTestReference = alphaTestReference; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/AlphaTestOp.cs b/Ryujinx.Graphics.Shader/AlphaTestOp.cs new file mode 100644 index 000000000..57c0d1314 --- /dev/null +++ b/Ryujinx.Graphics.Shader/AlphaTestOp.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Shader +{ + public enum AlphaTestOp + { + Never = 1, + Less, + Equal, + LessOrEqual, + Greater, + NotEqual, + GreaterOrEqual, + Always + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 29795cdff..87288b2e6 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -34,6 +34,16 @@ namespace Ryujinx.Graphics.Shader /// Span of the memory location ReadOnlySpan GetCode(ulong address, int minimumSize); + AlphaTestOp QueryAlphaTestCompare() + { + return AlphaTestOp.Always; + } + + float QueryAlphaTestReference() + { + return 0f; + } + AttributeType QueryAttributeType(int location) { return AttributeType.Float; diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index d99389727..237a66aa2 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -234,6 +234,8 @@ namespace Ryujinx.Graphics.Shader.Translation } else if (Config.Stage == ShaderStage.Fragment) { + bool supportsBgra = Config.GpuAccessor.QueryHostSupportsBgraFormat(); + if (Config.OmapDepth) { Operand dest = Attribute(AttributeConsts.FragmentOutputDepth); @@ -243,7 +245,40 @@ namespace Ryujinx.Graphics.Shader.Translation this.Copy(dest, src); } - bool supportsBgra = Config.GpuAccessor.QueryHostSupportsBgraFormat(); + AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare(); + + if (alphaTestOp != AlphaTestOp.Always && (Config.OmapTargets & 8) != 0) + { + if (alphaTestOp == AlphaTestOp.Never) + { + this.Discard(); + } + else + { + Instruction comparator = alphaTestOp switch + { + AlphaTestOp.Equal => Instruction.CompareEqual, + AlphaTestOp.Greater => Instruction.CompareGreater, + AlphaTestOp.GreaterOrEqual => Instruction.CompareGreaterOrEqual, + AlphaTestOp.Less => Instruction.CompareLess, + AlphaTestOp.LessOrEqual => Instruction.CompareLessOrEqual, + AlphaTestOp.NotEqual => Instruction.CompareNotEqual, + _ => 0 + }; + + Debug.Assert(comparator != 0, $"Invalid alpha test operation \"{alphaTestOp}\"."); + + Operand alpha = Register(3, RegisterType.Gpr); + Operand alphaRef = ConstF(Config.GpuAccessor.QueryAlphaTestReference()); + Operand alphaPass = Add(Instruction.FP32 | comparator, Local(), alpha, alphaRef); + Operand alphaPassLabel = Label(); + + this.BranchIfTrue(alphaPassLabel, alphaPass); + this.Discard(); + this.MarkLabel(alphaPassLabel); + } + } + int regIndexBase = 0; for (int rtIndex = 0; rtIndex < 8; rtIndex++)