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++)