diff --git a/Ryujinx.Graphics.GAL/BlendFactor.cs b/Ryujinx.Graphics.GAL/BlendFactor.cs
index 135873e9..4149ad51 100644
--- a/Ryujinx.Graphics.GAL/BlendFactor.cs
+++ b/Ryujinx.Graphics.GAL/BlendFactor.cs
@@ -38,4 +38,25 @@ namespace Ryujinx.Graphics.GAL
Src1AlphaGl = 0xc902,
OneMinusSrc1AlphaGl = 0xc903
}
+
+ public static class BlendFactorExtensions
+ {
+ public static bool IsDualSource(this BlendFactor factor)
+ {
+ switch (factor)
+ {
+ case BlendFactor.Src1Color:
+ case BlendFactor.Src1ColorGl:
+ case BlendFactor.Src1Alpha:
+ case BlendFactor.Src1AlphaGl:
+ case BlendFactor.OneMinusSrc1Color:
+ case BlendFactor.OneMinusSrc1ColorGl:
+ case BlendFactor.OneMinusSrc1Alpha:
+ case BlendFactor.OneMinusSrc1AlphaGl:
+ return true;
+ }
+
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs
index 62df15e7..a8af5497 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs
@@ -328,5 +328,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Signal();
}
}
+
+ ///
+ /// Sets the dual-source blend enabled state.
+ ///
+ /// True if blending is enabled and using dual-source blend
+ public void SetDualSourceBlendEnabled(bool enabled)
+ {
+ if (enabled != _graphics.DualSourceBlendEnable)
+ {
+ _graphics.DualSourceBlendEnable = enabled;
+
+ Signal();
+ }
+ }
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index d7d197ad..00e09a31 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -1183,6 +1183,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool blendIndependent = _state.State.BlendIndependent;
ColorF blendConstant = _state.State.BlendConstant;
+ bool dualSourceBlendEnabled = false;
+
if (blendIndependent)
{
for (int index = 0; index < Constants.TotalRenderTargets; index++)
@@ -1200,6 +1202,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
FilterBlendFactor(blend.AlphaSrcFactor, index),
FilterBlendFactor(blend.AlphaDstFactor, index));
+ if (enable &&
+ (blend.ColorSrcFactor.IsDualSource() ||
+ blend.ColorDstFactor.IsDualSource() ||
+ blend.AlphaSrcFactor.IsDualSource() ||
+ blend.AlphaDstFactor.IsDualSource()))
+ {
+ dualSourceBlendEnabled = true;
+ }
+
_pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
}
@@ -1219,12 +1230,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
FilterBlendFactor(blend.AlphaSrcFactor, 0),
FilterBlendFactor(blend.AlphaDstFactor, 0));
+ if (enable &&
+ (blend.ColorSrcFactor.IsDualSource() ||
+ blend.ColorDstFactor.IsDualSource() ||
+ blend.AlphaSrcFactor.IsDualSource() ||
+ blend.AlphaDstFactor.IsDualSource()))
+ {
+ dualSourceBlendEnabled = true;
+ }
+
for (int index = 0; index < Constants.TotalRenderTargets; index++)
{
_pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
}
}
+
+ _currentSpecState.SetDualSourceBlendEnabled(dualSourceBlendEnabled);
}
///
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
index 97173c96..17639ca1 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
@@ -141,6 +141,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
}
+ ///
+ public bool QueryDualSourceBlendEnable()
+ {
+ return _oldSpecState.GraphicsState.DualSourceBlendEnable;
+ }
+
///
public InputTopology QueryPrimitiveTopology()
{
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index edc5a8a0..0b87cc91 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
- private const uint CodeGenVersion = 4368;
+ private const uint CodeGenVersion = 4404;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 05631a21..3e816733 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -157,6 +157,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
}
+ ///
+ public bool QueryDualSourceBlendEnable()
+ {
+ return _state.GraphicsState.DualSourceBlendEnable;
+ }
+
///
public InputTopology QueryPrimitiveTopology()
{
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
index 70ac5017..5247a096 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
@@ -92,6 +92,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
///
public Array8 FragmentOutputTypes;
+ ///
+ /// Indicates whether dual source blend is enabled.
+ ///
+ public bool DualSourceBlendEnable;
+
///
/// Creates a new GPU graphics state.
///
@@ -111,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0
/// Indicates that any storage buffer use is unaligned
/// Type of the fragment shader outputs
+ /// Type of the vertex attributes consumed by the shader
public GpuChannelGraphicsState(
bool earlyZForce,
PrimitiveTopology topology,
@@ -127,7 +133,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
ref Array32 attributeTypes,
bool hasConstantBufferDrawParameters,
bool hasUnalignedStorageBuffer,
- ref Array8 fragmentOutputTypes)
+ ref Array8 fragmentOutputTypes,
+ bool dualSourceBlendEnable)
{
EarlyZForce = earlyZForce;
Topology = topology;
@@ -145,6 +152,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
FragmentOutputTypes = fragmentOutputTypes;
+ DualSourceBlendEnable = dualSourceBlendEnable;
}
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
index 856507cd..b2c4fccd 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
@@ -535,6 +535,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false;
}
+ if (graphicsState.DualSourceBlendEnable != GraphicsState.DualSourceBlendEnable)
+ {
+ return false;
+ }
+
return Matches(channel, ref poolState, checkTextures, isCompute: false);
}
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index 62996bd0..6b6d0289 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -833,31 +833,13 @@ namespace Ryujinx.Graphics.OpenGL
(BlendingFactorSrc)blend.AlphaSrcFactor.Convert(),
(BlendingFactorDest)blend.AlphaDstFactor.Convert());
- static bool IsDualSource(BlendFactor factor)
- {
- switch (factor)
- {
- case BlendFactor.Src1Color:
- case BlendFactor.Src1ColorGl:
- case BlendFactor.Src1Alpha:
- case BlendFactor.Src1AlphaGl:
- case BlendFactor.OneMinusSrc1Color:
- case BlendFactor.OneMinusSrc1ColorGl:
- case BlendFactor.OneMinusSrc1Alpha:
- case BlendFactor.OneMinusSrc1AlphaGl:
- return true;
- }
-
- return false;
- }
-
EnsureFramebuffer();
_framebuffer.SetDualSourceBlend(
- IsDualSource(blend.ColorSrcFactor) ||
- IsDualSource(blend.ColorDstFactor) ||
- IsDualSource(blend.AlphaSrcFactor) ||
- IsDualSource(blend.AlphaDstFactor));
+ blend.ColorSrcFactor.IsDualSource() ||
+ blend.ColorDstFactor.IsDualSource() ||
+ blend.AlphaSrcFactor.IsDualSource() ||
+ blend.AlphaDstFactor.IsDualSource());
if (_blendConstant != blend.BlendConstant)
{
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 996d312b..9032ca59 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -612,6 +612,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
else
{
int usedAttributes = context.Config.UsedOutputAttributes;
+
+ if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
+ {
+ int firstOutput = BitOperations.TrailingZeroCount(usedAttributes);
+ int mask = 3 << firstOutput;
+
+ if ((usedAttributes & mask) == mask)
+ {
+ usedAttributes &= ~mask;
+ DeclareOutputDualSourceBlendAttribute(context, firstOutput);
+ }
+ }
+
while (usedAttributes != 0)
{
int index = BitOperations.TrailingZeroCount(usedAttributes);
@@ -690,6 +703,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
}
+ private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int attr)
+ {
+ string name = $"{DefaultNames.OAttributePrefix}{attr}";
+ string name2 = $"{DefaultNames.OAttributePrefix}{(attr + 1)}";
+
+ context.AppendLine($"layout (location = {attr}, index = 0) out vec4 {name};");
+ context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};");
+ }
+
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet attrs)
{
foreach (int attr in attrs.Order())
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index 5108d861..df42a13c 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Numerics;
using static Spv.Specification;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
@@ -622,7 +623,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
{
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
- context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
+
+ if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
+ {
+ int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes);
+ int index = location - firstLocation;
+ int mask = 3 << firstLocation;
+
+ if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask)
+ {
+ context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
+ context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
+ }
+ else
+ {
+ context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
+ }
+ }
+ else
+ {
+ context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
+ }
}
if (!isOutAttr)
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index f364437c..ba5f2a92 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -205,6 +205,15 @@ namespace Ryujinx.Graphics.Shader
return false;
}
+ ///
+ /// Queries dual source blend state.
+ ///
+ /// True if blending is enabled with a dual source blend equation, false otherwise
+ bool QueryDualSourceBlendEnable()
+ {
+ return false;
+ }
+
///
/// Queries host about the presence of the FrontFacing built-in variable bug.
///